diff --git a/funcs/games/blackjack.py b/funcs/games/blackjack.py index b3d4379..1d68b0d 100644 --- a/funcs/games/blackjack.py +++ b/funcs/games/blackjack.py @@ -3,11 +3,11 @@ import math import datetime import asyncio import discord +from PIL import Image, ImageDraw, ImageFont from shutil import copyfile from utils import replaceMultiple -from .blackjackDraw import DrawBlackjack class Blackjack(): def __init__(self,bot): @@ -795,7 +795,7 @@ class Blackjack(): else: self.bot.log("Ending loop on round "+str(gameRound),str(channel.id)) -# Returning current hi-lo value + # Returning current hi-lo value async def hilo(self, ctx): channel = ctx.channel_id data = self.bot.database["hilo"].find_one({"_id":str(channel)}) @@ -825,4 +825,142 @@ class Blackjack(): decksLeft = round(cardsLeft/52,1) await ctx.send(f"Cards left:\n{cardsLeft} cards, {decksLeft} decks", hidden=True) +class DrawBlackjack(): + def __init__(self,bot): + self.bot = bot + self.BORDER = 100 + self.PLACEMENT = [0,0] + self.ROTATION = 0 + + def drawImage(self,channel): + self.bot.log("Drawing blackjack table",channel) + game = self.bot.database["blackjack games"].find_one({"_id":channel}) + + fnt = ImageFont.truetype('resources/fonts/futura-bold.ttf', 50) + fntSmol = ImageFont.truetype('resources/fonts/futura-bold.ttf', 40) + self.BORDERSmol = int(self.BORDER/3.5) + + table = Image.open("resources/games/blackjackTable.png") + self.PLACEMENT = [2,1,3,0,4] + textImage = ImageDraw.Draw(table) + hands = game["user hands"] + + dealerBusted = game["dealer busted"] + dealerBlackjack = game["dealer blackjack"] + + try: + if game["all standing"] == False: + dealerHand = self.drawHand(game["dealer hand"],True,False,False) + else: + dealerHand = self.drawHand(game["dealer hand"],False,dealerBusted,dealerBlackjack) + except: + self.bot.log("Error drawing dealer hand (error code 1341a)") + + table.paste(dealerHand,(800-self.BORDERSmol,20-self.BORDERSmol),dealerHand) + + for x in range(len(hands)): + key, value = list(hands.items())[x] + key = self.bot.databaseFuncs.getName(key) + #self.bot.log("Drawing "+key+"'s hand") + userHand = self.drawHand(value["hand"],False,value["busted"],value["blackjack"]) + try: + if value["split"] == 3: + table.paste(userHand,(32-self.BORDERSmol+(384*self.PLACEMENT[x]),280-self.BORDERSmol),userHand) + userOtherHand = self.drawHand(value["other hand"]["hand"],False,value["other hand"]["busted"],value["other hand"]["blackjack"]) + table.paste(userOtherHand,(32-self.BORDERSmol+(384*self.PLACEMENT[x]),420-self.BORDERSmol),userOtherHand) + userThirdHand = self.drawHand(value["third hand"]["hand"],False,value["third hand"]["busted"],value["third hand"]["blackjack"]) + table.paste(userThirdHand,(32-self.BORDERSmol+(384*self.PLACEMENT[x]),560-self.BORDERSmol),userThirdHand) + userFourthHand = self.drawHand(value["fourth hand"]["hand"],False,value["fourth hand"]["busted"],value["fourth hand"]["blackjack"]) + table.paste(userFourthHand,(32-self.BORDERSmol+(384*self.PLACEMENT[x]),700-self.BORDERSmol),userFourthHand) + elif value["split"] == 2: + table.paste(userHand,(32-self.BORDERSmol+(384*self.PLACEMENT[x]),420-self.BORDERSmol),userHand) + userOtherHand = self.drawHand(value["other hand"]["hand"],False,value["other hand"]["busted"],value["other hand"]["blackjack"]) + table.paste(userOtherHand,(32-self.BORDERSmol+(384*self.PLACEMENT[x]),560-self.BORDERSmol),userOtherHand) + userThirdHand = self.drawHand(value["third hand"]["hand"],False,value["third hand"]["busted"],value["third hand"]["blackjack"]) + table.paste(userThirdHand,(32-self.BORDERSmol+(384*self.PLACEMENT[x]),700-self.BORDERSmol),userThirdHand) + elif value["split"] == 1: + table.paste(userHand,(32-self.BORDERSmol+(384*self.PLACEMENT[x]),560-self.BORDERSmol),userHand) + userOtherHand = self.drawHand(value["other hand"]["hand"],False,value["other hand"]["busted"],value["other hand"]["blackjack"]) + table.paste(userOtherHand,(32-self.BORDERSmol+(384*self.PLACEMENT[x]),700-self.BORDERSmol),userOtherHand) + else: + table.paste(userHand,(32-self.BORDERSmol+(384*self.PLACEMENT[x]),680-self.BORDERSmol),userHand) + except: + self.bot.log("Error drawing player hands (error code 1341b)") + + textWidth = fnt.getsize(key)[0] + if textWidth < 360: + textImage.text((32+(384*self.PLACEMENT[x])+117-int(textWidth/2)-3,1010-3),key,fill=(0,0,0), font=fnt) + textImage.text((32+(384*self.PLACEMENT[x])+117-int(textWidth/2)+3,1010-3),key,fill=(0,0,0), font=fnt) + textImage.text((32+(384*self.PLACEMENT[x])+117-int(textWidth/2)-3,1010+3),key,fill=(0,0,0), font=fnt) + textImage.text((32+(384*self.PLACEMENT[x])+117-int(textWidth/2)+3,1010+3),key,fill=(0,0,0), font=fnt) + textImage.text((32+(384*self.PLACEMENT[x])+117-int(textWidth/2),1005),key,fill=(255,255,255), font=fnt) + else: + textWidth = fntSmol.getsize(key)[0] + textImage.text((32+(384*self.PLACEMENT[x])+117-int(textWidth/2)-2,1020-2),key,fill=(0,0,0), font=fntSmol) + textImage.text((32+(384*self.PLACEMENT[x])+117-int(textWidth/2)+2,1020-2),key,fill=(0,0,0), font=fntSmol) + textImage.text((32+(384*self.PLACEMENT[x])+117-int(textWidth/2)-2,1020+2),key,fill=(0,0,0), font=fntSmol) + textImage.text((32+(384*self.PLACEMENT[x])+117-int(textWidth/2)+2,1020+2),key,fill=(0,0,0), font=fntSmol) + textImage.text((32+(384*self.PLACEMENT[x])+117-int(textWidth/2),1015),key,fill=(255,255,255), font=fntSmol) + + self.bot.log("Saving table image") + table.save("resources/games/blackjackTables/blackjackTable"+channel+".png") + + return + + def drawHand(self, hand, dealer, busted, blackjack): + self.bot.log("Drawing hand "+str(hand)+", "+str(busted)+", "+str(blackjack)) + fnt = ImageFont.truetype('resources/fonts/futura-bold.ttf', 200) + fnt2 = ImageFont.truetype('resources/fonts/futura-bold.ttf', 120) + length = len(hand) + background = Image.new("RGBA", ((self.BORDER*2)+691+(125*(length-1)),(self.BORDER*2)+1065),(0,0,0,0)) + textImage = ImageDraw.Draw(background) + + if dealer: + img = Image.open("resources/games/cards/"+hand[0].upper()+".png") + #self.ROTATION = (random.randint(-20,20)/10.0) + img = img.rotate(self.ROTATION,expand = 1) + #self.PLACEMENT = [random.randint(-20,20),random.randint(-20,20)] + background.paste(img,(self.BORDER+self.PLACEMENT[0],self.BORDER+self.PLACEMENT[1]),img) + img = Image.open("resources/games/cards/red_back.png") + #self.ROTATION = (random.randint(-20,20)/10.0) + img = img.rotate(self.ROTATION,expand = 1) + #self.PLACEMENT = [random.randint(-20,20),random.randint(-20,20)] + background.paste(img,(125+self.BORDER+self.PLACEMENT[0],self.BORDER+self.PLACEMENT[1]),img) + else: + for x in range(length): + img = Image.open("resources/games/cards/"+hand[x].upper()+".png") + #self.ROTATION = (random.randint(-20,20)/10.0) + img = img.rotate(self.ROTATION,expand = 1) + #self.PLACEMENT = [random.randint(-20,20),random.randint(-20,20)] + background.paste(img,(self.BORDER+(x*125)+self.PLACEMENT[0],self.BORDER+self.PLACEMENT[1]),img) + + w, h = background.size + textHeight = 290+self.BORDER + + #self.bot.log("Drawing busted/blackjack") + if busted: + textWidth = fnt.getsize("BUSTED")[0] + textImage.text((int(w/2)-int(textWidth/2)-10,textHeight+20-10),"BUSTED",fill=(0,0,0), font=fnt) + textImage.text((int(w/2)-int(textWidth/2)+10,textHeight+20-10),"BUSTED",fill=(0,0,0), font=fnt) + textImage.text((int(w/2)-int(textWidth/2)-10,textHeight+20+10),"BUSTED",fill=(0,0,0), font=fnt) + textImage.text((int(w/2)-int(textWidth/2)+10,textHeight+20+10),"BUSTED",fill=(0,0,0), font=fnt) + textImage.text((int(w/2)-int(textWidth/2)-5,textHeight-5),"BUSTED",fill=(255,255,255), font=fnt) + textImage.text((int(w/2)-int(textWidth/2)+5,textHeight-5),"BUSTED",fill=(255,255,255), font=fnt) + textImage.text((int(w/2)-int(textWidth/2)-5,textHeight+5),"BUSTED",fill=(255,255,225), font=fnt) + textImage.text((int(w/2)-int(textWidth/2)+5,textHeight+5),"BUSTED",fill=(255,255,255), font=fnt) + textImage.text((int(w/2)-int(textWidth/2),textHeight),"BUSTED",fill=(255,50,50), font=fnt) + elif blackjack: + textWidth = fnt2.getsize("BLACKJACK")[0] + textImage.text((int(w/2)-int(textWidth/2)-6,textHeight+20-6),"BLACKJACK",fill=(0,0,0), font=fnt2) + textImage.text((int(w/2)-int(textWidth/2)+6,textHeight+20-6),"BLACKJACK",fill=(0,0,0), font=fnt2) + textImage.text((int(w/2)-int(textWidth/2)-6,textHeight+20+6),"BLACKJACK",fill=(0,0,0), font=fnt2) + textImage.text((int(w/2)-int(textWidth/2)+6,textHeight+20+6),"BLACKJACK",fill=(0,0,0), font=fnt2) + textImage.text((int(w/2)-int(textWidth/2)-3,textHeight-3),"BLACKJACK",fill=(255,255,255), font=fnt2) + textImage.text((int(w/2)-int(textWidth/2)+3,textHeight-3),"BLACKJACK",fill=(255,255,255), font=fnt2) + textImage.text((int(w/2)-int(textWidth/2)-3,textHeight+3),"BLACKJACK",fill=(255,255,255), font=fnt2) + textImage.text((int(w/2)-int(textWidth/2)+3,textHeight+3),"BLACKJACK",fill=(255,255,255), font=fnt2) + textImage.text((int(w/2)-int(textWidth/2),textHeight),"BLACKJACK",fill=(155,123,0), font=fnt2) + + #self.bot.log("Returning resized image") + return background.resize((int(w/3.5),int(h/3.5)),resample=Image.BILINEAR) diff --git a/funcs/games/blackjackDraw.py b/funcs/games/blackjackDraw.py deleted file mode 100644 index 23aae9e..0000000 --- a/funcs/games/blackjackDraw.py +++ /dev/null @@ -1,141 +0,0 @@ -from PIL import Image, ImageDraw, ImageFont - -border = 100 -placement = [0,0] -rotation = 0 - -class DrawBlackjack(): - def __init__(self,bot): - self.bot = bot - - def drawImage(self,channel): - self.bot.log("Drawing blackjack table",channel) - game = self.bot.database["blackjack games"].find_one({"_id":channel}) - - fnt = ImageFont.truetype('resources/fonts/futura-bold.ttf', 50) - fntSmol = ImageFont.truetype('resources/fonts/futura-bold.ttf', 40) - borderSmol = int(border/3.5) - - table = Image.open("resources/games/blackjackTable.png") - placement = [2,1,3,0,4] - textImage = ImageDraw.Draw(table) - hands = game["user hands"] - - dealerBusted = game["dealer busted"] - dealerBlackjack = game["dealer blackjack"] - - try: - if game["all standing"] == False: - dealerHand = self.drawHand(game["dealer hand"],True,False,False) - else: - dealerHand = self.drawHand(game["dealer hand"],False,dealerBusted,dealerBlackjack) - except: - self.bot.log("Error drawing dealer hand (error code 1341a)") - - table.paste(dealerHand,(800-borderSmol,20-borderSmol),dealerHand) - - for x in range(len(hands)): - key, value = list(hands.items())[x] - key = self.bot.databaseFuncs.getName(key) - #self.bot.log("Drawing "+key+"'s hand") - userHand = self.drawHand(value["hand"],False,value["busted"],value["blackjack"]) - try: - if value["split"] == 3: - table.paste(userHand,(32-borderSmol+(384*placement[x]),280-borderSmol),userHand) - userOtherHand = self.drawHand(value["other hand"]["hand"],False,value["other hand"]["busted"],value["other hand"]["blackjack"]) - table.paste(userOtherHand,(32-borderSmol+(384*placement[x]),420-borderSmol),userOtherHand) - userThirdHand = self.drawHand(value["third hand"]["hand"],False,value["third hand"]["busted"],value["third hand"]["blackjack"]) - table.paste(userThirdHand,(32-borderSmol+(384*placement[x]),560-borderSmol),userThirdHand) - userFourthHand = self.drawHand(value["fourth hand"]["hand"],False,value["fourth hand"]["busted"],value["fourth hand"]["blackjack"]) - table.paste(userFourthHand,(32-borderSmol+(384*placement[x]),700-borderSmol),userFourthHand) - elif value["split"] == 2: - table.paste(userHand,(32-borderSmol+(384*placement[x]),420-borderSmol),userHand) - userOtherHand = self.drawHand(value["other hand"]["hand"],False,value["other hand"]["busted"],value["other hand"]["blackjack"]) - table.paste(userOtherHand,(32-borderSmol+(384*placement[x]),560-borderSmol),userOtherHand) - userThirdHand = self.drawHand(value["third hand"]["hand"],False,value["third hand"]["busted"],value["third hand"]["blackjack"]) - table.paste(userThirdHand,(32-borderSmol+(384*placement[x]),700-borderSmol),userThirdHand) - elif value["split"] == 1: - table.paste(userHand,(32-borderSmol+(384*placement[x]),560-borderSmol),userHand) - userOtherHand = self.drawHand(value["other hand"]["hand"],False,value["other hand"]["busted"],value["other hand"]["blackjack"]) - table.paste(userOtherHand,(32-borderSmol+(384*placement[x]),700-borderSmol),userOtherHand) - else: - table.paste(userHand,(32-borderSmol+(384*placement[x]),680-borderSmol),userHand) - except: - self.bot.log("Error drawing player hands (error code 1341b)") - - textWidth = fnt.getsize(key)[0] - if textWidth < 360: - textImage.text((32+(384*placement[x])+117-int(textWidth/2)-3,1010-3),key,fill=(0,0,0), font=fnt) - textImage.text((32+(384*placement[x])+117-int(textWidth/2)+3,1010-3),key,fill=(0,0,0), font=fnt) - textImage.text((32+(384*placement[x])+117-int(textWidth/2)-3,1010+3),key,fill=(0,0,0), font=fnt) - textImage.text((32+(384*placement[x])+117-int(textWidth/2)+3,1010+3),key,fill=(0,0,0), font=fnt) - textImage.text((32+(384*placement[x])+117-int(textWidth/2),1005),key,fill=(255,255,255), font=fnt) - else: - textWidth = fntSmol.getsize(key)[0] - textImage.text((32+(384*placement[x])+117-int(textWidth/2)-2,1020-2),key,fill=(0,0,0), font=fntSmol) - textImage.text((32+(384*placement[x])+117-int(textWidth/2)+2,1020-2),key,fill=(0,0,0), font=fntSmol) - textImage.text((32+(384*placement[x])+117-int(textWidth/2)-2,1020+2),key,fill=(0,0,0), font=fntSmol) - textImage.text((32+(384*placement[x])+117-int(textWidth/2)+2,1020+2),key,fill=(0,0,0), font=fntSmol) - textImage.text((32+(384*placement[x])+117-int(textWidth/2),1015),key,fill=(255,255,255), font=fntSmol) - - self.bot.log("Saving table image") - table.save("resources/games/blackjackTables/blackjackTable"+channel+".png") - - return - - def drawHand(self, hand, dealer, busted, blackjack): - self.bot.log("Drawing hand "+str(hand)+", "+str(busted)+", "+str(blackjack)) - fnt = ImageFont.truetype('resources/fonts/futura-bold.ttf', 200) - fnt2 = ImageFont.truetype('resources/fonts/futura-bold.ttf', 120) - length = len(hand) - background = Image.new("RGBA", ((border*2)+691+(125*(length-1)),(border*2)+1065),(0,0,0,0)) - textImage = ImageDraw.Draw(background) - - if dealer: - img = Image.open("resources/games/cards/"+hand[0].upper()+".png") - #rotation = (random.randint(-20,20)/10.0) - img = img.rotate(rotation,expand = 1) - #placement = [random.randint(-20,20),random.randint(-20,20)] - background.paste(img,(border+placement[0],border+placement[1]),img) - img = Image.open("resources/games/cards/red_back.png") - #rotation = (random.randint(-20,20)/10.0) - img = img.rotate(rotation,expand = 1) - #placement = [random.randint(-20,20),random.randint(-20,20)] - background.paste(img,(125+border+placement[0],border+placement[1]),img) - else: - for x in range(length): - img = Image.open("resources/games/cards/"+hand[x].upper()+".png") - #rotation = (random.randint(-20,20)/10.0) - img = img.rotate(rotation,expand = 1) - #placement = [random.randint(-20,20),random.randint(-20,20)] - background.paste(img,(border+(x*125)+placement[0],border+placement[1]),img) - - w, h = background.size - textHeight = 290+border - - #self.bot.log("Drawing busted/blackjack") - if busted: - textWidth = fnt.getsize("BUSTED")[0] - textImage.text((int(w/2)-int(textWidth/2)-10,textHeight+20-10),"BUSTED",fill=(0,0,0), font=fnt) - textImage.text((int(w/2)-int(textWidth/2)+10,textHeight+20-10),"BUSTED",fill=(0,0,0), font=fnt) - textImage.text((int(w/2)-int(textWidth/2)-10,textHeight+20+10),"BUSTED",fill=(0,0,0), font=fnt) - textImage.text((int(w/2)-int(textWidth/2)+10,textHeight+20+10),"BUSTED",fill=(0,0,0), font=fnt) - textImage.text((int(w/2)-int(textWidth/2)-5,textHeight-5),"BUSTED",fill=(255,255,255), font=fnt) - textImage.text((int(w/2)-int(textWidth/2)+5,textHeight-5),"BUSTED",fill=(255,255,255), font=fnt) - textImage.text((int(w/2)-int(textWidth/2)-5,textHeight+5),"BUSTED",fill=(255,255,225), font=fnt) - textImage.text((int(w/2)-int(textWidth/2)+5,textHeight+5),"BUSTED",fill=(255,255,255), font=fnt) - textImage.text((int(w/2)-int(textWidth/2),textHeight),"BUSTED",fill=(255,50,50), font=fnt) - elif blackjack: - textWidth = fnt2.getsize("BLACKJACK")[0] - textImage.text((int(w/2)-int(textWidth/2)-6,textHeight+20-6),"BLACKJACK",fill=(0,0,0), font=fnt2) - textImage.text((int(w/2)-int(textWidth/2)+6,textHeight+20-6),"BLACKJACK",fill=(0,0,0), font=fnt2) - textImage.text((int(w/2)-int(textWidth/2)-6,textHeight+20+6),"BLACKJACK",fill=(0,0,0), font=fnt2) - textImage.text((int(w/2)-int(textWidth/2)+6,textHeight+20+6),"BLACKJACK",fill=(0,0,0), font=fnt2) - textImage.text((int(w/2)-int(textWidth/2)-3,textHeight-3),"BLACKJACK",fill=(255,255,255), font=fnt2) - textImage.text((int(w/2)-int(textWidth/2)+3,textHeight-3),"BLACKJACK",fill=(255,255,255), font=fnt2) - textImage.text((int(w/2)-int(textWidth/2)-3,textHeight+3),"BLACKJACK",fill=(255,255,255), font=fnt2) - textImage.text((int(w/2)-int(textWidth/2)+3,textHeight+3),"BLACKJACK",fill=(255,255,255), font=fnt2) - textImage.text((int(w/2)-int(textWidth/2),textHeight),"BLACKJACK",fill=(155,123,0), font=fnt2) - - #self.bot.log("Returning resized image") - return background.resize((int(w/3.5),int(h/3.5)),resample=Image.BILINEAR) diff --git a/funcs/games/connectFour.py b/funcs/games/connectFour.py index a01ec11..88a8024 100644 --- a/funcs/games/connectFour.py +++ b/funcs/games/connectFour.py @@ -3,26 +3,25 @@ import copy import math import discord -from .connectFourDraw import drawConnectFour - -AISCORES = { - "middle": 3, - "two in a row": 10, - "three in a row": 50, - "enemy two in a row": -35, - "enemy three in a row": -200, - "enemy win": -10000, - "win": 10000, - "avoid losing": 100 -} - -ROWCOUNT = 6 -COLUMNCOUNT = 7 +from PIL import Image, ImageDraw, ImageFont class ConnectFour(): def __init__(self,bot): self.bot = bot self.draw = drawConnectFour(bot) + self.AISCORES = { + "middle": 3, + "two in a row": 10, + "three in a row": 50, + "enemy two in a row": -35, + "enemy three in a row": -200, + "enemy win": -10000, + "win": 10000, + "avoid losing": 100 + } + + self.ROWCOUNT = 6 + self.COLUMNCOUNT = 7 # Starts the game async def start(self, ctx, opponent): @@ -74,7 +73,7 @@ class ConnectFour(): canStart = False if canStart: - board = [[0 for _ in range(COLUMNCOUNT)] for _ in range(ROWCOUNT)] + board = [[0 for _ in range(self.COLUMNCOUNT)] for _ in range(self.ROWCOUNT)] players = [user, opponent] random.shuffle(players) @@ -261,13 +260,13 @@ class ConnectFour(): winDirection = "" winCoordinates = [0,0] - for row in range(ROWCOUNT): - for place in range(COLUMNCOUNT): + for row in range(self.ROWCOUNT): + for place in range(self.COLUMNCOUNT): if won == 0: piecePlayer = board[row][place] if piecePlayer != 0: # Checks horizontal - if place <= COLUMNCOUNT-4: + if place <= self.COLUMNCOUNT-4: pieces = [board[row][place+1],board[row][place+2],board[row][place+3]] else: pieces = [0] @@ -278,7 +277,7 @@ class ConnectFour(): winCoordinates = [row,place] # Checks vertical - if row <= ROWCOUNT-4: + if row <= self.ROWCOUNT-4: pieces = [board[row+1][place],board[row+2][place],board[row+3][place]] else: pieces = [0] @@ -289,7 +288,7 @@ class ConnectFour(): winCoordinates = [row,place] # Checks right diagonal - if row <= ROWCOUNT-4 and place <= COLUMNCOUNT-4: + if row <= self.ROWCOUNT-4 and place <= self.COLUMNCOUNT-4: pieces = [board[row+1][place+1],board[row+2][place+2],board[row+3][place+3]] else: pieces = [0] @@ -300,7 +299,7 @@ class ConnectFour(): winCoordinates = [row,place] # Checks left diagonal - if row <= ROWCOUNT-4 and place >= 3: + if row <= self.ROWCOUNT-4 and place >= 3: pieces = [board[row+1][place-1],board[row+2][place-2],board[row+3][place-3]] else: pieces = [0] @@ -324,8 +323,8 @@ class ConnectFour(): player = game["players"].index(f"#{self.bot.user.id}")+1 difficulty = game["difficulty"] - scores = [-math.inf for _ in range(COLUMNCOUNT)] - for column in range(COLUMNCOUNT): + scores = [-math.inf for _ in range(self.COLUMNCOUNT)] + for column in range(self.COLUMNCOUNT): testBoard = copy.deepcopy(board) testBoard = self.placeOnBoard(testBoard,player,column) if testBoard != None: @@ -351,28 +350,28 @@ class ConnectFour(): # Adds points for middle placement # Checks horizontal - for row in range(ROWCOUNT): + for row in range(self.ROWCOUNT): if board[row][3] == player: - score += AISCORES["middle"] + score += self.AISCORES["middle"] rowArray = [int(i) for i in list(board[row])] - for place in range(COLUMNCOUNT-3): + for place in range(self.COLUMNCOUNT-3): window = rowArray[place:place+4] score += self.evaluateWindow(window,player,otherPlayer) # Checks Vertical - for column in range(COLUMNCOUNT): + for column in range(self.COLUMNCOUNT): columnArray = [int(i[column]) for i in list(board)] - for place in range(ROWCOUNT-3): + for place in range(self.ROWCOUNT-3): window = columnArray[place:place+4] score += self.evaluateWindow(window,player,otherPlayer) # Checks right diagonal - for row in range(ROWCOUNT-3): - for place in range(COLUMNCOUNT-3): + for row in range(self.ROWCOUNT-3): + for place in range(self.COLUMNCOUNT-3): window = [board[row][place],board[row+1][place+1],board[row+2][place+2],board[row+3][place+3]] score += self.evaluateWindow(window,player,otherPlayer) - for place in range(3,COLUMNCOUNT): + for place in range(3,self.COLUMNCOUNT): window = [board[row][place],board[row+1][place-1],board[row+2][place-2],board[row+3][place-3]] score += self.evaluateWindow(window,player,otherPlayer) @@ -382,19 +381,19 @@ class ConnectFour(): ## Add points if AI wins #if won == player: - # score += AISCORES["win"] + # score += self.AISCORES["win"] return score def evaluateWindow(self, window,player,otherPlayer): if window.count(player) == 4: - return AISCORES["win"] + return self.AISCORES["win"] elif window.count(player) == 3 and window.count(0) == 1: - return AISCORES["three in a row"] + return self.AISCORES["three in a row"] elif window.count(player) == 2 and window.count(0) == 2: - return AISCORES["two in a row"] + return self.AISCORES["two in a row"] elif window.count(otherPlayer) == 4: - return AISCORES["enemy win"] + return self.AISCORES["enemy win"] else: return 0 @@ -407,25 +406,178 @@ class ConnectFour(): return points if maximizingPlayer: value = -math.inf - for column in range(0,COLUMNCOUNT): + for column in range(0,self.COLUMNCOUNT): testBoard = copy.deepcopy(board) testBoard = self.placeOnBoard(testBoard,player,column) if testBoard != None: evaluation = await self.minimax(testBoard,depth-1,player%2+1,originalPlayer,alpha,beta,False) - if evaluation < -9000: evaluation += AISCORES["avoid losing"] + if evaluation < -9000: evaluation += self.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): + for column in range(0,self.COLUMNCOUNT): testBoard = copy.deepcopy(board) testBoard = self.placeOnBoard(testBoard,player,column) if testBoard != None: evaluation = await self.minimax(testBoard,depth-1,player%2+1,originalPlayer,alpha,beta,True) - if evaluation < -9000: evaluation += AISCORES["avoid losing"] + if evaluation < -9000: evaluation += self.AISCORES["avoid losing"] value = min(value,evaluation) beta = min(beta,evaluation) if beta <= alpha: break return value + +class drawConnectFour(): + def __init__(self, bot): + self.bot = bot + + # Draws the whole thing + def drawImage(self, channel): + self.bot.log("Drawing connect four board") + game = self.bot.database["connect 4 games"].find_one({"_id":channel}) + + board = game["board"] + + border = 40 + gridBorder = 40 + cornerSize = 300 + boardOutlineSize = 10 + pieceOutlineSize = 10 + emptyOutlineSize = 0 + bottomBorder = 110 + exampleCircles = 100 + w, h = 2800,2400 + backgroundColor = (230,230,234,255) + boardOutlineColor = (0,0,0) + pieceOutlineColor = (244,244,248) + emptyOutlineColor = (0,0,0) + player1Color = (254,74,73) + player2Color = (254,215,102) + boardColor = (42,183,202) + placeSize = 300 + white = (255,255,255,160) + winBarColor = (250,250,250,255) + + fnt = ImageFont.truetype('resources/fonts/futura-bold.ttf', exampleCircles) + + boardSize = [w-(2*(border+gridBorder)),h-(2*(border+gridBorder))] + placeGridSize = [math.floor(boardSize[0]/7),math.floor(boardSize[1]/6)] + pieceStartx = (border+gridBorder)+math.floor(placeGridSize[0]/2)-math.floor(placeSize/2) + pieceStarty = (border+gridBorder)+math.floor(placeGridSize[1]/2)-math.floor(placeSize/2) + + if game["players"][0] == "Gwendolyn": + player1 = "Gwendolyn" + else: + player1 = self.bot.databaseFuncs.getName(game["players"][0]) + + if game["players"][1] == "Gwendolyn": + player2 = "Gwendolyn" + else: + player2 = self.bot.databaseFuncs.getName(game["players"][1]) + + + background = Image.new("RGB", (w,h+bottomBorder),backgroundColor) + d = ImageDraw.Draw(background,"RGBA") + + # This whole part was the easiest way to make a rectangle with rounded corners and an outline + # - Corners: + d.ellipse([(border,border),(border+cornerSize,border+cornerSize)],fill=boardColor,outline=boardOutlineColor,width=boardOutlineSize) + d.ellipse([(w-(border+cornerSize),h-(border+cornerSize)),(w-border,h-border)],fill=boardColor,outline=boardOutlineColor,width=boardOutlineSize) + d.ellipse([(border,h-(border+cornerSize)),(border+cornerSize,h-border)],fill=boardColor,outline=boardOutlineColor,width=boardOutlineSize) + d.ellipse([(w-(border+cornerSize),border),(w-border,border+cornerSize)],fill=boardColor,outline=boardOutlineColor,width=boardOutlineSize) + # - Rectangle: + d.rectangle([(border+math.floor(cornerSize/2),border),(w-(border+math.floor(cornerSize/2)),h-border)],fill=boardColor,outline=boardOutlineColor,width=boardOutlineSize) + d.rectangle([(border,border+math.floor(cornerSize/2)),(w-border,h-(border+math.floor(cornerSize/2)))],fill=boardColor,outline=boardOutlineColor,width=boardOutlineSize) + # - Removing outline on the inside: + d.rectangle([(border+math.floor(cornerSize/2),border+math.floor(cornerSize/2)),(w-(border+math.floor(cornerSize/2)),h-(border+math.floor(cornerSize/2)))],fill=boardColor) + d.rectangle([(border+math.floor(cornerSize/2),border+boardOutlineSize),(w-(border+math.floor(cornerSize/2)),h-(border+boardOutlineSize))],fill=boardColor) + d.rectangle([(border+boardOutlineSize,border+math.floor(cornerSize/2)),(w-(border+boardOutlineSize),h-(border+math.floor(cornerSize/2)))],fill=boardColor) + + for x, line in enumerate(board): + for y, piece in enumerate(line): + + if piece == 1: + pieceColor = player1Color + outlineWidth = pieceOutlineSize + outlineColor = pieceOutlineColor + elif piece == 2: + pieceColor = player2Color + outlineWidth = pieceOutlineSize + outlineColor = pieceOutlineColor + else: + pieceColor = backgroundColor + outlineWidth = emptyOutlineSize + outlineColor = emptyOutlineColor + + startx = pieceStartx + placeGridSize[0]*y + starty = pieceStarty + placeGridSize[1]*x + + d.ellipse([(startx,starty),(startx+placeSize,starty+placeSize)],fill=pieceColor,outline=outlineColor,width=outlineWidth) + + if game["winner"] != 0: + coordinates = game["win coordinates"] + startx = border + placeGridSize[0]*coordinates[1] + gridBorder + starty = border + placeGridSize[1]*coordinates[0] + gridBorder + a = (placeGridSize[0]*4-gridBorder-border)**2 + b = (placeGridSize[1]*4-gridBorder-border)**2 + diagonalLength = (math.sqrt(a+b))/placeGridSize[0] + diagonalAngle = math.degrees(math.atan(placeGridSize[1]/placeGridSize[0])) + + if game["win direction"] == "h": + winBar = Image.new("RGBA",(placeGridSize[0]*4,placeGridSize[1]),(0,0,0,0)) + winD = ImageDraw.Draw(winBar) + winD.ellipse([(0,0),(placeGridSize[0],placeGridSize[1])],fill=white) + winD.ellipse([((placeGridSize[0]*3),0),(placeGridSize[0]*4,placeGridSize[1])],fill=white) + winD.rectangle([(int(placeGridSize[0]*0.5),0),(int(placeGridSize[0]*3.5),placeGridSize[1])],fill=white) + + elif game["win direction"] == "v": + winBar = Image.new("RGBA",(placeGridSize[0],placeGridSize[1]*4),(0,0,0,0)) + winD = ImageDraw.Draw(winBar) + winD.ellipse([(0,0),(placeGridSize[0],placeGridSize[1])],fill=white) + winD.ellipse([(0,(placeGridSize[1]*3)),(placeGridSize[0],placeGridSize[1]*4)],fill=white) + winD.rectangle([0,(int(placeGridSize[1]*0.5)),(placeGridSize[0],int(placeGridSize[1]*3.5))],fill=white) + + elif game["win direction"] == "r": + winBar = Image.new("RGBA",(int(placeGridSize[0]*diagonalLength),placeGridSize[1]),(0,0,0,0)) + winD = ImageDraw.Draw(winBar) + winD.ellipse([(0,0),(placeGridSize[0],placeGridSize[1])],fill=white) + winD.ellipse([((placeGridSize[0]*(diagonalLength-1)),0),(placeGridSize[0]*diagonalLength,placeGridSize[1])],fill=white) + winD.rectangle([(int(placeGridSize[0]*0.5),0),(int(placeGridSize[0]*(diagonalLength-0.5)),placeGridSize[1])],fill=white) + winBar = winBar.rotate(-diagonalAngle,expand=1) + startx -= 90 + starty -= 100 + + elif game["win direction"] == "l": + winBar = Image.new("RGBA",(int(placeGridSize[0]*diagonalLength),placeGridSize[1]),(0,0,0,0)) + winD = ImageDraw.Draw(winBar) + winD.ellipse([(0,0),(placeGridSize[0],placeGridSize[1])],fill=white) + winD.ellipse([((placeGridSize[0]*(diagonalLength-1)),0),(placeGridSize[0]*diagonalLength,placeGridSize[1])],fill=white) + winD.rectangle([(int(placeGridSize[0]*0.5),0),(int(placeGridSize[0]*(diagonalLength-0.5)),placeGridSize[1])],fill=white) + winBar = winBar.rotate(diagonalAngle,expand=1) + startx -= placeGridSize[0]*3 + 90 + starty -= gridBorder + 60 + + + mask = winBar.copy()#.convert("L") + #mask.putalpha(128) + #mask.save("test.png") + + winBarImage = Image.new("RGBA",mask.size,color=winBarColor) + background.paste(winBarImage,(startx,starty),mask) + + # Bottom + textPadding = 20 + + exampleHeight = h - border + int((bottomBorder+border)/2) - int(exampleCircles/2) + d.ellipse([(border,exampleHeight),(border+exampleCircles),(exampleHeight+exampleCircles)],fill=player1Color,outline=boardOutlineColor,width=3) + d.text((border+exampleCircles+textPadding,exampleHeight),player1,font=fnt,fill=(0,0,0)) + + textWidth = fnt.getsize(player2)[0] + d.ellipse([(w-border-exampleCircles-textWidth-textPadding,exampleHeight),(w-border-textWidth-textPadding),(exampleHeight+exampleCircles)],fill=player2Color,outline=boardOutlineColor,width=3) + d.text((w-border-textWidth,exampleHeight),player2,font=fnt,fill=(0,0,0)) + + + background.save("resources/games/connect4Boards/board"+channel+".png") + diff --git a/funcs/games/connectFourDraw.py b/funcs/games/connectFourDraw.py deleted file mode 100644 index 340bb2f..0000000 --- a/funcs/games/connectFourDraw.py +++ /dev/null @@ -1,155 +0,0 @@ -import math - -from PIL import Image, ImageDraw, ImageFont - -class drawConnectFour(): - def __init__(self, bot): - self.bot = bot - - # Draws the whole thing - def drawImage(self, channel): - self.bot.log("Drawing connect four board") - game = self.bot.database["connect 4 games"].find_one({"_id":channel}) - - board = game["board"] - - border = 40 - gridBorder = 40 - cornerSize = 300 - boardOutlineSize = 10 - pieceOutlineSize = 10 - emptyOutlineSize = 0 - bottomBorder = 110 - exampleCircles = 100 - w, h = 2800,2400 - backgroundColor = (230,230,234,255) - boardOutlineColor = (0,0,0) - pieceOutlineColor = (244,244,248) - emptyOutlineColor = (0,0,0) - player1Color = (254,74,73) - player2Color = (254,215,102) - boardColor = (42,183,202) - placeSize = 300 - white = (255,255,255,160) - winBarColor = (250,250,250,255) - - fnt = ImageFont.truetype('resources/fonts/futura-bold.ttf', exampleCircles) - - boardSize = [w-(2*(border+gridBorder)),h-(2*(border+gridBorder))] - placeGridSize = [math.floor(boardSize[0]/7),math.floor(boardSize[1]/6)] - pieceStartx = (border+gridBorder)+math.floor(placeGridSize[0]/2)-math.floor(placeSize/2) - pieceStarty = (border+gridBorder)+math.floor(placeGridSize[1]/2)-math.floor(placeSize/2) - - if game["players"][0] == "Gwendolyn": - player1 = "Gwendolyn" - else: - player1 = self.bot.databaseFuncs.getName(game["players"][0]) - - if game["players"][1] == "Gwendolyn": - player2 = "Gwendolyn" - else: - player2 = self.bot.databaseFuncs.getName(game["players"][1]) - - - background = Image.new("RGB", (w,h+bottomBorder),backgroundColor) - d = ImageDraw.Draw(background,"RGBA") - - # This whole part was the easiest way to make a rectangle with rounded corners and an outline - # - Corners: - d.ellipse([(border,border),(border+cornerSize,border+cornerSize)],fill=boardColor,outline=boardOutlineColor,width=boardOutlineSize) - d.ellipse([(w-(border+cornerSize),h-(border+cornerSize)),(w-border,h-border)],fill=boardColor,outline=boardOutlineColor,width=boardOutlineSize) - d.ellipse([(border,h-(border+cornerSize)),(border+cornerSize,h-border)],fill=boardColor,outline=boardOutlineColor,width=boardOutlineSize) - d.ellipse([(w-(border+cornerSize),border),(w-border,border+cornerSize)],fill=boardColor,outline=boardOutlineColor,width=boardOutlineSize) - # - Rectangle: - d.rectangle([(border+math.floor(cornerSize/2),border),(w-(border+math.floor(cornerSize/2)),h-border)],fill=boardColor,outline=boardOutlineColor,width=boardOutlineSize) - d.rectangle([(border,border+math.floor(cornerSize/2)),(w-border,h-(border+math.floor(cornerSize/2)))],fill=boardColor,outline=boardOutlineColor,width=boardOutlineSize) - # - Removing outline on the inside: - d.rectangle([(border+math.floor(cornerSize/2),border+math.floor(cornerSize/2)),(w-(border+math.floor(cornerSize/2)),h-(border+math.floor(cornerSize/2)))],fill=boardColor) - d.rectangle([(border+math.floor(cornerSize/2),border+boardOutlineSize),(w-(border+math.floor(cornerSize/2)),h-(border+boardOutlineSize))],fill=boardColor) - d.rectangle([(border+boardOutlineSize,border+math.floor(cornerSize/2)),(w-(border+boardOutlineSize),h-(border+math.floor(cornerSize/2)))],fill=boardColor) - - for x, line in enumerate(board): - for y, piece in enumerate(line): - - if piece == 1: - pieceColor = player1Color - outlineWidth = pieceOutlineSize - outlineColor = pieceOutlineColor - elif piece == 2: - pieceColor = player2Color - outlineWidth = pieceOutlineSize - outlineColor = pieceOutlineColor - else: - pieceColor = backgroundColor - outlineWidth = emptyOutlineSize - outlineColor = emptyOutlineColor - - startx = pieceStartx + placeGridSize[0]*y - starty = pieceStarty + placeGridSize[1]*x - - d.ellipse([(startx,starty),(startx+placeSize,starty+placeSize)],fill=pieceColor,outline=outlineColor,width=outlineWidth) - - if game["winner"] != 0: - coordinates = game["win coordinates"] - startx = border + placeGridSize[0]*coordinates[1] + gridBorder - starty = border + placeGridSize[1]*coordinates[0] + gridBorder - a = (placeGridSize[0]*4-gridBorder-border)**2 - b = (placeGridSize[1]*4-gridBorder-border)**2 - diagonalLength = (math.sqrt(a+b))/placeGridSize[0] - diagonalAngle = math.degrees(math.atan(placeGridSize[1]/placeGridSize[0])) - - if game["win direction"] == "h": - winBar = Image.new("RGBA",(placeGridSize[0]*4,placeGridSize[1]),(0,0,0,0)) - winD = ImageDraw.Draw(winBar) - winD.ellipse([(0,0),(placeGridSize[0],placeGridSize[1])],fill=white) - winD.ellipse([((placeGridSize[0]*3),0),(placeGridSize[0]*4,placeGridSize[1])],fill=white) - winD.rectangle([(int(placeGridSize[0]*0.5),0),(int(placeGridSize[0]*3.5),placeGridSize[1])],fill=white) - - elif game["win direction"] == "v": - winBar = Image.new("RGBA",(placeGridSize[0],placeGridSize[1]*4),(0,0,0,0)) - winD = ImageDraw.Draw(winBar) - winD.ellipse([(0,0),(placeGridSize[0],placeGridSize[1])],fill=white) - winD.ellipse([(0,(placeGridSize[1]*3)),(placeGridSize[0],placeGridSize[1]*4)],fill=white) - winD.rectangle([0,(int(placeGridSize[1]*0.5)),(placeGridSize[0],int(placeGridSize[1]*3.5))],fill=white) - - elif game["win direction"] == "r": - winBar = Image.new("RGBA",(int(placeGridSize[0]*diagonalLength),placeGridSize[1]),(0,0,0,0)) - winD = ImageDraw.Draw(winBar) - winD.ellipse([(0,0),(placeGridSize[0],placeGridSize[1])],fill=white) - winD.ellipse([((placeGridSize[0]*(diagonalLength-1)),0),(placeGridSize[0]*diagonalLength,placeGridSize[1])],fill=white) - winD.rectangle([(int(placeGridSize[0]*0.5),0),(int(placeGridSize[0]*(diagonalLength-0.5)),placeGridSize[1])],fill=white) - winBar = winBar.rotate(-diagonalAngle,expand=1) - startx -= 90 - starty -= 100 - - elif game["win direction"] == "l": - winBar = Image.new("RGBA",(int(placeGridSize[0]*diagonalLength),placeGridSize[1]),(0,0,0,0)) - winD = ImageDraw.Draw(winBar) - winD.ellipse([(0,0),(placeGridSize[0],placeGridSize[1])],fill=white) - winD.ellipse([((placeGridSize[0]*(diagonalLength-1)),0),(placeGridSize[0]*diagonalLength,placeGridSize[1])],fill=white) - winD.rectangle([(int(placeGridSize[0]*0.5),0),(int(placeGridSize[0]*(diagonalLength-0.5)),placeGridSize[1])],fill=white) - winBar = winBar.rotate(diagonalAngle,expand=1) - startx -= placeGridSize[0]*3 + 90 - starty -= gridBorder + 60 - - - mask = winBar.copy()#.convert("L") - #mask.putalpha(128) - #mask.save("test.png") - - winBarImage = Image.new("RGBA",mask.size,color=winBarColor) - background.paste(winBarImage,(startx,starty),mask) - - # Bottom - textPadding = 20 - - exampleHeight = h - border + int((bottomBorder+border)/2) - int(exampleCircles/2) - d.ellipse([(border,exampleHeight),(border+exampleCircles),(exampleHeight+exampleCircles)],fill=player1Color,outline=boardOutlineColor,width=3) - d.text((border+exampleCircles+textPadding,exampleHeight),player1,font=fnt,fill=(0,0,0)) - - textWidth = fnt.getsize(player2)[0] - d.ellipse([(w-border-exampleCircles-textWidth-textPadding,exampleHeight),(w-border-textWidth-textPadding),(exampleHeight+exampleCircles)],fill=player2Color,outline=boardOutlineColor,width=3) - d.text((w-border-textWidth,exampleHeight),player2,font=fnt,fill=(0,0,0)) - - - background.save("resources/games/connect4Boards/board"+channel+".png") diff --git a/funcs/games/hangman.py b/funcs/games/hangman.py index df94339..f330000 100644 --- a/funcs/games/hangman.py +++ b/funcs/games/hangman.py @@ -1,13 +1,13 @@ import json, urllib, datetime, string, discord +import math, random -from .hangmanDraw import DrawHangman - -apiUrl = "https://api.wordnik.com/v4/words.json/randomWords?hasDictionaryDef=true&minCorpusCount=5000&maxCorpusCount=-1&minDictionaryCount=1&maxDictionaryCount=-1&minLength=3&maxLength=11&limit=1&api_key=" +from PIL import ImageDraw, Image, ImageFont class Hangman(): def __init__(self,bot): self.bot = bot self.draw = DrawHangman(bot) + self.APIURL = "https://api.wordnik.com/v4/words.json/randomWords?hasDictionaryDef=true&minCorpusCount=5000&maxCorpusCount=-1&minDictionaryCount=1&maxDictionaryCount=-1&minLength=3&maxLength=11&limit=1&api_key=" async def start(self, ctx): await self.bot.defer(ctx) @@ -21,7 +21,7 @@ class Hangman(): apiKey = self.bot.credentials.wordnikKey word = "-" while "-" in word or "." in word: - with urllib.request.urlopen(apiUrl+apiKey) as p: + with urllib.request.urlopen(self.APIURL+apiKey) as p: word = list(json.load(p)[0]["word"].upper()) self.bot.log("Found the word \""+"".join(word)+"\"") guessed = [False] * len(word) @@ -162,4 +162,246 @@ class Hangman(): emoji = chr(ord(letter)+127397) await message.add_reaction(emoji) +class DrawHangman(): + def __init__(self,bot): + self.bot = bot + self.CIRCLESIZE = 120 + self.LINEWIDTH = 12 + self.BODYSIZE =210 + self.LIMBSIZE = 60 + self.ARMPOSITION = 60 + + self.MANX = (self.LIMBSIZE*2)+self.LINEWIDTH*4 + self.MANY = (self.CIRCLESIZE+self.BODYSIZE+self.LIMBSIZE)+self.LINEWIDTH*4 + + self.LETTERLINELENGTH = 90 + self.LETTERLINEDISTANCE = 30 + + self.GALLOWX, self.GALLOWY = 360,600 + self.GOLDENRATIO = 1-(1 / ((1 + 5 ** 0.5) / 2)) + + LETTERSIZE = 75 + TEXTSIZE = 70 + self.FONT = ImageFont.truetype('resources/fonts/comic-sans-bold.ttf', LETTERSIZE) + self.SMALLFONT = ImageFont.truetype('resources/fonts/comic-sans-bold.ttf', TEXTSIZE) + + def calcDeviance(self,preDev,preDevAcc,posChange,maxmin,maxAcceleration): + devAcc = preDevAcc + random.uniform(-posChange,posChange) + if devAcc > maxmin * maxAcceleration: devAcc = maxmin * maxAcceleration + elif devAcc < -maxmin * maxAcceleration: devAcc = -maxmin * maxAcceleration + + dev = preDev + devAcc + if dev > maxmin: dev = maxmin + elif dev < -maxmin: dev = -maxmin + return dev, devAcc + + def badCircle(self): + background = Image.new("RGBA",(self.CIRCLESIZE+(self.LINEWIDTH*3),self.CIRCLESIZE+(self.LINEWIDTH*3)),color=(0,0,0,0)) + + d = ImageDraw.Draw(background,"RGBA") + middle = (self.CIRCLESIZE+(self.LINEWIDTH*3))/2 + devx = 0 + devy = 0 + devAccx = 0 + devAccy = 0 + start = random.randint(-100,-80) + degreesAmount = 360 + random.randint(-10,30) + + for degree in range(degreesAmount): + devx, devAccx = self.calcDeviance(devx,devAccx,self.LINEWIDTH/100,self.LINEWIDTH,0.03) + devy, devAccy = self.calcDeviance(devy,devAccy,self.LINEWIDTH/100,self.LINEWIDTH,0.03) + + x = middle + (math.cos(math.radians(degree+start)) * (self.CIRCLESIZE/2)) - (self.LINEWIDTH/2) + devx + y = middle + (math.sin(math.radians(degree+start)) * (self.CIRCLESIZE/2)) - (self.LINEWIDTH/2) + devy + + d.ellipse([(x,y),(x+self.LINEWIDTH,y+self.LINEWIDTH)],fill=(0,0,0,255)) + + return background + + def badLine(self, length, rotated = False): + if rotated: + w, h = length+self.LINEWIDTH*3, self.LINEWIDTH*3 + else: + w, h = self.LINEWIDTH*3,length+self.LINEWIDTH*3 + background = Image.new("RGBA",(w,h),color=(0,0,0,0)) + + d = ImageDraw.Draw(background,"RGBA") + devx = random.randint(-int(self.LINEWIDTH/3),int(self.LINEWIDTH/3)) + devy = 0 + devAccx = 0 + devAccy = 0 + + for pixel in range(length): + devx, devAccx = self.calcDeviance(devx,devAccx,self.LINEWIDTH/1000,self.LINEWIDTH,0.004) + devy, devAccy = self.calcDeviance(devy,devAccy,self.LINEWIDTH/1000,self.LINEWIDTH,0.004) + + if rotated: + x = self.LINEWIDTH + pixel + devx + y = self.LINEWIDTH + devy + else: + x = self.LINEWIDTH + devx + y = self.LINEWIDTH + pixel + devy + + d.ellipse([(x,y),(x+self.LINEWIDTH,y+self.LINEWIDTH)],fill=(0,0,0,255)) + + return background + + def drawMan(self, misses): + background = Image.new("RGBA",(self.MANX,self.MANY),color=(0,0,0,0)) + + if misses >= 1: + head = self.badCircle() + background.paste(head,(int((self.MANX-(self.CIRCLESIZE+(self.LINEWIDTH*3)))/2),0),head) + if misses >= 2: + body = self.badLine(self.BODYSIZE) + background.paste(body,(int((self.MANX-(self.LINEWIDTH*3))/2),self.CIRCLESIZE),body) + + if misses >= 3: + limbs = random.sample(["rl","ll","ra","la"],min(misses-2,4)) + else: limbs = [] + + for limb in limbs: + if limb == "ra": + limbDrawing = self.badLine(self.LIMBSIZE,True) + rotation = random.randint(-45,45) + xpos = int((self.MANX-(self.LINEWIDTH*3))/2) + rotationCompensation = min(-int(math.sin(math.radians(rotation))*(self.LIMBSIZE+(self.LINEWIDTH*3))),0) + ypos = self.CIRCLESIZE+self.ARMPOSITION + rotationCompensation + limbDrawing = limbDrawing.rotate(rotation,expand=1) + background.paste(limbDrawing,(xpos,ypos),limbDrawing) + elif limb == "la": + limbDrawing = self.badLine(self.LIMBSIZE,True) + rotation = random.randint(-45,45) + xpos = int((self.MANX-(self.LINEWIDTH*3))/2)-self.LIMBSIZE + rotationCompensation = min(int(math.sin(math.radians(rotation))*(self.LIMBSIZE+(self.LINEWIDTH*3))),0) + ypos = self.CIRCLESIZE+self.ARMPOSITION + rotationCompensation + limbDrawing = limbDrawing.rotate(rotation,expand=1) + background.paste(limbDrawing,(xpos,ypos),limbDrawing) + elif limb == "rl": + limbDrawing = self.badLine(self.LIMBSIZE,True) + rotation = random.randint(-15,15) + xpos = int((self.MANX-(self.LINEWIDTH*3))/2)-self.LINEWIDTH + ypos = self.CIRCLESIZE+self.BODYSIZE-self.LINEWIDTH + limbDrawing = limbDrawing.rotate(rotation-45,expand=1) + background.paste(limbDrawing,(xpos,ypos),limbDrawing) + elif limb == "ll": + limbDrawing = self.badLine(self.LIMBSIZE,True) + rotation = random.randint(-15,15) + limbDrawing = limbDrawing.rotate(rotation+45,expand=1) + xpos = int((self.MANX-(self.LINEWIDTH*3))/2)-limbDrawing.size[0]+self.LINEWIDTH*3 + ypos = self.CIRCLESIZE+self.BODYSIZE + background.paste(limbDrawing,(xpos,ypos),limbDrawing) + + return background + + def badText(self, text, big, color=(0,0,0,255)): + if big: font = self.FONT + else: font = self.SMALLFONT + w, h = font.getsize(text) + img = Image.new("RGBA",(w,h),color=(0,0,0,0)) + d = ImageDraw.Draw(img,"RGBA") + + d.text((0,0),text,font=font,fill=color) + return img + + def drawGallows(self): + background = Image.new("RGBA",(self.GALLOWX,self.GALLOWY),color=(0,0,0,0)) + + bottomLine = self.badLine(int(self.GALLOWX*0.75),True) + background.paste(bottomLine,(int(self.GALLOWX*0.125),self.GALLOWY-(self.LINEWIDTH*4)),bottomLine) + + lineTwo = self.badLine(self.GALLOWY-self.LINEWIDTH*6) + background.paste(lineTwo,(int(self.GALLOWX*(0.75*self.GOLDENRATIO)),self.LINEWIDTH*2),lineTwo) + + topLine = self.badLine(int(self.GALLOWY*0.30),True) + background.paste(topLine,(int(self.GALLOWX*(0.75*self.GOLDENRATIO))-self.LINEWIDTH,self.LINEWIDTH*3),topLine) + + lastLine = self.badLine(int(self.GALLOWY*0.125)) + background.paste(lastLine,((int(self.GALLOWX*(0.75*self.GOLDENRATIO))+int(self.GALLOWY*0.30)-self.LINEWIDTH),self.LINEWIDTH*3),lastLine) + return background + + def drawLetterLines(self, word,guessed,misses): + letterLines = Image.new("RGBA",((self.LETTERLINELENGTH+self.LETTERLINEDISTANCE)*len(word),self.LETTERLINELENGTH+self.LINEWIDTH*3),color=(0,0,0,0)) + for x, letter in enumerate(word): + line = self.badLine(self.LETTERLINELENGTH,True) + letterLines.paste(line,(x*(self.LETTERLINELENGTH+self.LETTERLINEDISTANCE),self.LETTERLINELENGTH),line) + if guessed[x]: + letterDrawing = self.badText(letter,True) + letterWidth = self.FONT.getsize(letter)[0] + letterx = int(x*(self.LETTERLINELENGTH+self.LETTERLINEDISTANCE)-(letterWidth/2)+(self.LETTERLINELENGTH*0.5)+(self.LINEWIDTH*2)) + letterLines.paste(letterDrawing,(letterx,0),letterDrawing) + elif misses == 6: + letterDrawing = self.badText(letter,True,(242,66,54)) + letterWidth = self.FONT.getsize(letter)[0] + letterx = int(x*(self.LETTERLINELENGTH+self.LETTERLINEDISTANCE)-(letterWidth/2)+(self.LETTERLINELENGTH*0.5)+(self.LINEWIDTH*2)) + letterLines.paste(letterDrawing,(letterx,0),letterDrawing) + + return letterLines + + def shortestDist(self,positions,newPosition): + shortestDist = math.inf + x, y = newPosition + for i, j in positions: + xdist = abs(i-x) + ydist = abs(j-y) + dist = math.sqrt(xdist**2+ydist**2) + if shortestDist > dist: shortestDist = dist + return shortestDist + + def drawMisses(self,guesses,word): + background = Image.new("RGBA",(600,400),color=(0,0,0,0)) + pos = [] + for guess in guesses: + if guess not in word: + placed = False + while placed == False: + letter = self.badText(guess,True) + w, h = self.FONT.getsize(guess) + x = random.randint(0,600-w) + y = random.randint(0,400-h) + if self.shortestDist(pos,(x,y)) > 70: + pos.append((x,y)) + background.paste(letter,(x,y),letter) + placed = True + return background + + def drawImage(self,channel): + self.bot.log("Drawing hangman image", channel) + game = self.bot.database["hangman games"].find_one({"_id":channel}) + + random.seed(game["game ID"]) + + background = Image.open("resources/paper.jpg") + try: + gallow = self.drawGallows() + except: + self.bot.log("Error drawing gallows (error code 1711)") + + try: + man = self.drawMan(game["misses"]) + except: + self.bot.log("Error drawing stick figure (error code 1712)") + + random.seed(game["game ID"]) + try: + letterLines = self.drawLetterLines(game["word"],game["guessed"],game["misses"]) + except: + self.bot.log("error drawing letter lines (error code 1713)") + + random.seed(game["game ID"]) + try: + misses = self.drawMisses(game["guessed letters"],game["word"]) + except: + self.bot.log("Error drawing misses (error code 1714)") + + background.paste(gallow,(100,100),gallow) + background.paste(man,(300,210),man) + background.paste(letterLines,(120,840),letterLines) + background.paste(misses,(600,150),misses) + + missesText = self.badText("MISSES",False) + missesTextWidth = missesText.size[0] + background.paste(missesText,(850-int(missesTextWidth/2),50),missesText) + + background.save("resources/games/hangmanBoards/hangmanBoard"+channel+".png") diff --git a/funcs/games/hangmanDraw.py b/funcs/games/hangmanDraw.py deleted file mode 100644 index c955fee..0000000 --- a/funcs/games/hangmanDraw.py +++ /dev/null @@ -1,252 +0,0 @@ -import math, random - -from PIL import ImageDraw, Image, ImageFont - -circleDegrees = 360 -circleSize = 120 -lineWidth = 12 - -bodySize =210 -limbSize = 60 -armPosition = 60 - -manx = (limbSize*2)+lineWidth*4 -many = (circleSize+bodySize+limbSize)+lineWidth*4 - -letterSize = 75 -textSize = 70 - -letterLineLength = 90 -letterLineDistance = 30 - -gallowx, gallowy = 360,600 -goldenRatio = 1-(1 / ((1 + 5 ** 0.5) / 2)) - -fnt = ImageFont.truetype('resources/fonts/comic-sans-bold.ttf', letterSize) -smolfnt = ImageFont.truetype('resources/fonts/comic-sans-bold.ttf', textSize) - -backgroundColor = (255,255,255,255) - -class DrawHangman(): - def __init__(self,bot): - self.bot = bot - - def calcDeviance(self,preDev,preDevAcc,posChange,maxmin,maxAcceleration): - devAcc = preDevAcc + random.uniform(-posChange,posChange) - if devAcc > maxmin * maxAcceleration: devAcc = maxmin * maxAcceleration - elif devAcc < -maxmin * maxAcceleration: devAcc = -maxmin * maxAcceleration - - dev = preDev + devAcc - if dev > maxmin: dev = maxmin - elif dev < -maxmin: dev = -maxmin - return dev, devAcc - - def badCircle(self): - background = Image.new("RGBA",(circleSize+(lineWidth*3),circleSize+(lineWidth*3)),color=(0,0,0,0)) - - d = ImageDraw.Draw(background,"RGBA") - middle = (circleSize+(lineWidth*3))/2 - devx = 0 - devy = 0 - devAccx = 0 - devAccy = 0 - start = random.randint(-100,-80) - degreesAmount = circleDegrees + random.randint(-10,30) - - for degree in range(degreesAmount): - devx, devAccx = self.calcDeviance(devx,devAccx,lineWidth/100,lineWidth,0.03) - devy, devAccy = self.calcDeviance(devy,devAccy,lineWidth/100,lineWidth,0.03) - - x = middle + (math.cos(math.radians(degree+start)) * (circleSize/2)) - (lineWidth/2) + devx - y = middle + (math.sin(math.radians(degree+start)) * (circleSize/2)) - (lineWidth/2) + devy - - d.ellipse([(x,y),(x+lineWidth,y+lineWidth)],fill=(0,0,0,255)) - - return background - - def badLine(self, length, rotated = False): - if rotated: - w, h = length+lineWidth*3, lineWidth*3 - else: - w, h = lineWidth*3,length+lineWidth*3 - background = Image.new("RGBA",(w,h),color=(0,0,0,0)) - - d = ImageDraw.Draw(background,"RGBA") - devx = random.randint(-int(lineWidth/3),int(lineWidth/3)) - devy = 0 - devAccx = 0 - devAccy = 0 - - for pixel in range(length): - devx, devAccx = self.calcDeviance(devx,devAccx,lineWidth/1000,lineWidth,0.004) - devy, devAccy = self.calcDeviance(devy,devAccy,lineWidth/1000,lineWidth,0.004) - - if rotated: - x = lineWidth + pixel + devx - y = lineWidth + devy - else: - x = lineWidth + devx - y = lineWidth + pixel + devy - - d.ellipse([(x,y),(x+lineWidth,y+lineWidth)],fill=(0,0,0,255)) - - return background - - def drawMan(self, misses): - background = Image.new("RGBA",(manx,many),color=(0,0,0,0)) - - if misses >= 1: - head = self.badCircle() - background.paste(head,(int((manx-(circleSize+(lineWidth*3)))/2),0),head) - if misses >= 2: - body = self.badLine(bodySize) - background.paste(body,(int((manx-(lineWidth*3))/2),circleSize),body) - - if misses >= 3: - limbs = random.sample(["rl","ll","ra","la"],min(misses-2,4)) - else: limbs = [] - - for limb in limbs: - if limb == "ra": - limbDrawing = self.badLine(limbSize,True) - rotation = random.randint(-45,45) - xpos = int((manx-(lineWidth*3))/2) - rotationCompensation = min(-int(math.sin(math.radians(rotation))*(limbSize+(lineWidth*3))),0) - ypos = circleSize+armPosition + rotationCompensation - limbDrawing = limbDrawing.rotate(rotation,expand=1) - background.paste(limbDrawing,(xpos,ypos),limbDrawing) - elif limb == "la": - limbDrawing = self.badLine(limbSize,True) - rotation = random.randint(-45,45) - xpos = int((manx-(lineWidth*3))/2)-limbSize - rotationCompensation = min(int(math.sin(math.radians(rotation))*(limbSize+(lineWidth*3))),0) - ypos = circleSize+armPosition + rotationCompensation - limbDrawing = limbDrawing.rotate(rotation,expand=1) - background.paste(limbDrawing,(xpos,ypos),limbDrawing) - elif limb == "rl": - limbDrawing = self.badLine(limbSize,True) - rotation = random.randint(-15,15) - xpos = int((manx-(lineWidth*3))/2)-lineWidth - ypos = circleSize+bodySize-lineWidth - limbDrawing = limbDrawing.rotate(rotation-45,expand=1) - background.paste(limbDrawing,(xpos,ypos),limbDrawing) - elif limb == "ll": - limbDrawing = self.badLine(limbSize,True) - rotation = random.randint(-15,15) - limbDrawing = limbDrawing.rotate(rotation+45,expand=1) - xpos = int((manx-(lineWidth*3))/2)-limbDrawing.size[0]+lineWidth*3 - ypos = circleSize+bodySize - background.paste(limbDrawing,(xpos,ypos),limbDrawing) - - return background - - def badText(self, text, big, color=(0,0,0,255)): - if big: font = fnt - else: font = smolfnt - w, h = font.getsize(text) - img = Image.new("RGBA",(w,h),color=(0,0,0,0)) - d = ImageDraw.Draw(img,"RGBA") - - d.text((0,0),text,font=font,fill=color) - return img - - def drawGallows(self): - background = Image.new("RGBA",(gallowx,gallowy),color=(0,0,0,0)) - - bottomLine = self.badLine(int(gallowx*0.75),True) - background.paste(bottomLine,(int(gallowx*0.125),gallowy-(lineWidth*4)),bottomLine) - - lineTwo = self.badLine(gallowy-lineWidth*6) - background.paste(lineTwo,(int(gallowx*(0.75*goldenRatio)),lineWidth*2),lineTwo) - - topLine = self.badLine(int(gallowy*0.30),True) - background.paste(topLine,(int(gallowx*(0.75*goldenRatio))-lineWidth,lineWidth*3),topLine) - - lastLine = self.badLine(int(gallowy*0.125)) - background.paste(lastLine,((int(gallowx*(0.75*goldenRatio))+int(gallowy*0.30)-lineWidth),lineWidth*3),lastLine) - return background - - def drawLetterLines(self, word,guessed,misses): - letterLines = Image.new("RGBA",((letterLineLength+letterLineDistance)*len(word),letterLineLength+lineWidth*3),color=(0,0,0,0)) - for x, letter in enumerate(word): - line = self.badLine(letterLineLength,True) - letterLines.paste(line,(x*(letterLineLength+letterLineDistance),letterLineLength),line) - if guessed[x]: - letterDrawing = self.badText(letter,True) - letterWidth = fnt.getsize(letter)[0] - letterx = int(x*(letterLineLength+letterLineDistance)-(letterWidth/2)+(letterLineLength*0.5)+(lineWidth*2)) - letterLines.paste(letterDrawing,(letterx,0),letterDrawing) - elif misses == 6: - letterDrawing = self.badText(letter,True,(242,66,54)) - letterWidth = fnt.getsize(letter)[0] - letterx = int(x*(letterLineLength+letterLineDistance)-(letterWidth/2)+(letterLineLength*0.5)+(lineWidth*2)) - letterLines.paste(letterDrawing,(letterx,0),letterDrawing) - - return letterLines - - def shortestDist(self,positions,newPosition): - shortestDist = math.inf - x, y = newPosition - for i, j in positions: - xdist = abs(i-x) - ydist = abs(j-y) - dist = math.sqrt(xdist**2+ydist**2) - if shortestDist > dist: shortestDist = dist - return shortestDist - - def drawMisses(self,guesses,word): - background = Image.new("RGBA",(600,400),color=(0,0,0,0)) - pos = [] - for guess in guesses: - if guess not in word: - placed = False - while placed == False: - letter = self.badText(guess,True) - w, h = fnt.getsize(guess) - x = random.randint(0,600-w) - y = random.randint(0,400-h) - if self.shortestDist(pos,(x,y)) > 70: - pos.append((x,y)) - background.paste(letter,(x,y),letter) - placed = True - return background - - def drawImage(self,channel): - self.bot.log("Drawing hangman image", channel) - game = self.bot.database["hangman games"].find_one({"_id":channel}) - - random.seed(game["game ID"]) - - background = Image.open("resources/paper.jpg") - try: - gallow = self.drawGallows() - except: - self.bot.log("Error drawing gallows (error code 1711)") - - try: - man = self.drawMan(game["misses"]) - except: - self.bot.log("Error drawing stick figure (error code 1712)") - - random.seed(game["game ID"]) - try: - letterLines = self.drawLetterLines(game["word"],game["guessed"],game["misses"]) - except: - self.bot.log("error drawing letter lines (error code 1713)") - - random.seed(game["game ID"]) - try: - misses = self.drawMisses(game["guessed letters"],game["word"]) - except: - self.bot.log("Error drawing misses (error code 1714)") - - background.paste(gallow,(100,100),gallow) - background.paste(man,(300,210),man) - background.paste(letterLines,(120,840),letterLines) - background.paste(misses,(600,150),misses) - - missesText = self.badText("MISSES",False) - missesTextWidth = missesText.size[0] - background.paste(missesText,(850-int(missesTextWidth/2),50),missesText) - - background.save("resources/games/hangmanBoards/hangmanBoard"+channel+".png") diff --git a/funcs/games/hex.py b/funcs/games/hex.py index e6b8b5c..43f74d1 100644 --- a/funcs/games/hex.py +++ b/funcs/games/hex.py @@ -2,21 +2,21 @@ import random import copy import math import discord +import math -from .hexDraw import DrawHex - -BOARDWIDTH = 11 -ALL_POSITIONS = [(i,j) for i in range(11) for j in range(11)] -ALL_SET = set(ALL_POSITIONS) -EMPTY_DIJKSTRA = {} -for position in ALL_POSITIONS: - EMPTY_DIJKSTRA[position] = math.inf # an impossibly high number -HEX_DIRECTIONS = [(0,1),(-1,1),(-1,0),(0,-1),(1,-1),(1,0)] +from PIL import Image, ImageDraw, ImageFont class HexGame(): def __init__(self, bot): self.bot = bot self.draw = DrawHex(bot) + self.BOARDWIDTH = 11 + self.ALLPOSITIONS = [(i,j) for i in range(11) for j in range(11)] + self.ALLSET = set(self.ALLPOSITIONS) + self.EMPTYDIJKSTRA = {} + for position in self.ALLPOSITIONS: + self.EMPTYDIJKSTRA[position] = math.inf # an impossibly high number + self.HEXDIRECTIONS = [(0,1),(-1,1),(-1,0),(0,-1),(1,-1),(1,0)] async def surrender(self, ctx): channel = str(ctx.channel_id) @@ -30,7 +30,7 @@ class HexGame(): opponent = (players.index(user) + 1) % 2 opponentName = self.bot.databaseFuncs.getName(players[opponent]) self.bot.database["hex games"].update_one({"_id":channel},{"$set":{"winner":opponent + 1}}) - await ctx.send(f"{ctx.author.display_name} surrendered. That means {opponentName} won! Adding 30 Gwendobucks to their account") + await ctx.send(f"{ctx.author.display_name} surrendered") with open(f"resources/games/oldImages/hex{channel}", "r") as f: oldImage = await ctx.channel.fetch_message(int(f.read())) @@ -47,6 +47,8 @@ class HexGame(): with open(f"resources/games/oldImages/hex{channel}", "w") as f: f.write(str(oldImage.id)) + self.bot.database["hex games"].delete_one({"_id":channel}) + # Swap async def swap(self, ctx): channel = str(ctx.channel_id) @@ -149,7 +151,7 @@ class HexGame(): if canStart: # board is 11x11 - board = [[0 for i in range(BOARDWIDTH)] for j in range(BOARDWIDTH)] + board = [[0 for i in range(self.BOARDWIDTH)] for j in range(self.BOARDWIDTH)] players = [user, opponent] random.shuffle(players) # random starting player gameHistory = [] @@ -276,6 +278,8 @@ class HexGame(): if game["players"][winner-1] != f"#{self.bot.user.id}": winnings = game["difficulty"]*10 self.bot.money.addMoney(game["players"][winner-1].lower(),winnings) + + self.bot.database["hex games"].delete_one({"_id":channel}) else: with open(f"resources/games/oldImages/hex{channel}", "w") as f: f.write(str(oldImage.id)) @@ -290,7 +294,7 @@ class HexGame(): # Error handling 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): + if column not in range(self.BOARDWIDTH) or row not in range(self.BOARDWIDTH): self.bot.log("Position out of bounds") return None # Place at the position @@ -381,20 +385,20 @@ class HexGame(): winner = 0 # Here, I use Dijkstra's algorithm to evaluate the board, as proposed by this article: https://towardsdatascience.com/hex-creating-intelligent-adversaries-part-2-heuristics-dijkstras-algorithm-597e4dcacf93 for player in [1,2]: - Distance = copy.deepcopy(EMPTY_DIJKSTRA) + Distance = copy.deepcopy(self.EMPTYDIJKSTRA) # Initialize the starting hexes. For the blue player, this is the leftmost column. For the red player, this is the tom row. - for start in (ALL_POSITIONS[::11] if player == 2 else ALL_POSITIONS[:11]): + for start in (self.ALLPOSITIONS[::11] if player == 2 else self.ALLPOSITIONS[:11]): # An empty hex adds a of distance of 1. A hex of own color add distance 0. Opposite color adds infinite distance. Distance[start] = 1 if (board[start[0]][start[1]] == 0) else 0 if (board[start[0]][start[1]] == player) else math.inf visited = set() # Also called sptSet, short for "shortest path tree Set" - for _ in range(BOARDWIDTH**2): # We can at most check every 121 hexes + for _ in range(self.BOARDWIDTH**2): # We can at most check every 121 hexes # Find the next un-visited hex, that has the lowest distance - remainingHexes = ALL_SET.difference(visited) + remainingHexes = self.ALLSET.difference(visited) A = [Distance[k] for k in remainingHexes] # Find the distance to each un-visited hex u = list(remainingHexes)[A.index(min(A))] # Chooses the one with the lowest distance # Find neighbors of the hex u - for di in HEX_DIRECTIONS: + for di in self.HEXDIRECTIONS: v = (u[0] + di[0] , u[1] + di[1]) # v is a neighbor of u if v[0] in range(11) and v[1] in range(11) and v not in visited: new_dist = Distance[u] + (1 if (board[v[0]][v[1]] == 0) else 0 if (board[v[0]][v[1]] == player) else math.inf) @@ -425,7 +429,7 @@ class HexGame(): #self.bot.log("Judging a red move at depth {}".format(depth)) for i in possiblePlaces: testBoard = copy.deepcopy(board) - testBoard[i // BOARDWIDTH][i % BOARDWIDTH] = 1 # because maximizingPlayer is Red which is number 1 + testBoard[i // self.BOARDWIDTH][i % self.BOARDWIDTH] = 1 # because maximizingPlayer is Red which is number 1 evaluation = self.minimaxHex(testBoard,depth-1,alpha,beta,False) maxEval = max(maxEval, evaluation) alpha = max(alpha, evaluation) @@ -439,7 +443,7 @@ class HexGame(): #self.bot.log("Judging a blue move at depth {}".format(depth)) for i in possiblePlaces: testBoard = copy.deepcopy(board) - testBoard[i // BOARDWIDTH][i % BOARDWIDTH] = 2 # because minimizingPlayer is Blue which is number 2 + testBoard[i // self.BOARDWIDTH][i % self.BOARDWIDTH] = 2 # because minimizingPlayer is Blue which is number 2 evaluation = self.minimaxHex(testBoard,depth-1,alpha,beta,True) minEval = min(minEval, evaluation) beta = min(beta, evaluation) @@ -448,3 +452,192 @@ class HexGame(): break return minEval +class DrawHex(): + def __init__(self,bot): + self.bot = bot + # Defining all the variables + self.CANVASWIDTH = 2400 + self.CANVASHEIGHT = 1800 + self.SIDELENGTH = 75 + + # The offsets centers the board in the picture + self.XOFFSET = self.CANVASWIDTH/2 - 8*math.sqrt(3)*self.SIDELENGTH + + # The offsets are the coordinates of the upperleft point in the + # upperleftmost hexagon + self.YOFFSET = self.CANVASHEIGHT/2 - 8*self.SIDELENGTH + + # The whole width of one hexagon + self.HEXAGONWIDTH = math.sqrt(3) * self.SIDELENGTH + + # The height difference between two layers + self.HEXAGONHEIGHT = 1.5 * self.SIDELENGTH + self.FONTSIZE = 45 + self.TEXTCOLOR = (0,0,0) + self.FONT = ImageFont.truetype('resources/fonts/futura-bold.ttf', self.FONTSIZE) + + self.LINETHICKNESS = 15 + self.HEXTHICKNESS = 6 # This is half the width of the background lining between every hex + self.XTHICKNESS = self.HEXTHICKNESS * math.cos(math.pi/6) + self.YTHICKNESS = self.HEXTHICKNESS * math.sin(math.pi/6) + self.BACKGROUNDCOLOR = (230,230,230) + self.BETWEENCOLOR = (231,231,231) + self.BLANKCOLOR = "lightgrey" + self.PIECECOLOR = {1:(237,41,57),2:(0,165,255),0:self.BLANKCOLOR} # player1 is red, player2 is blue + self.BOARDCOORDINATES = [ [(self.XOFFSET + self.HEXAGONWIDTH*(column + row/2),self.YOFFSET + self.HEXAGONHEIGHT*row) for column in range(11)] for row in range(11)] # These are the coordinates for the upperleft corner of every hex + self.COLHEXTHICKNESS = 4 # When placing a hex, it is a little bigger than the underlying hex in the background (4 < 6) + self.COLXTHICKNESS = self.COLHEXTHICKNESS * math.cos(math.pi/6) + self.COLYTHICKNESS = self.COLHEXTHICKNESS * math.sin(math.pi/6) + # The Name display things: + self.NAMESIZE = 60 + self.NAMEFONT = ImageFont.truetype('resources/fonts/futura-bold.ttf', self.NAMESIZE) + self.XNAME = {1:175, 2:self.CANVASWIDTH-100} + self.YNAME = {1:self.CANVASHEIGHT-150, 2:150} + self.NAMEHEXPADDING = 90 + self.SMALLWIDTH = self.HEXAGONWIDTH * 0.6 + self.SMALLSIDELENGTH = self.SIDELENGTH * 0.6 + + def drawBoard(self, channel): + self.bot.log("Drawing empty Hex board") + + # Creates the empty image + im = Image.new('RGB', size=(self.CANVASWIDTH, self.CANVASHEIGHT),color = self.BACKGROUNDCOLOR) + # 'd' is a shortcut to drawing on the image + d = ImageDraw.Draw(im,"RGBA") + + # Drawing all the hexagons + for column in self.BOARDCOORDINATES: + for startingPoint in column: + x = startingPoint[0] + y = startingPoint[1] + d.polygon([ + (x, y), + (x+self.HEXAGONWIDTH/2, y-0.5*self.SIDELENGTH), + (x+self.HEXAGONWIDTH, y), + (x+self.HEXAGONWIDTH, y+self.SIDELENGTH), + (x+self.HEXAGONWIDTH/2, y+1.5*self.SIDELENGTH), + (x, y+self.SIDELENGTH), + ],fill = self.BETWEENCOLOR) + d.polygon([ + (x+self.XTHICKNESS, y + self.YTHICKNESS), + (x+self.HEXAGONWIDTH/2, y-0.5*self.SIDELENGTH + self.HEXTHICKNESS), + (x+self.HEXAGONWIDTH-self.XTHICKNESS, y + self.YTHICKNESS), + (x+self.HEXAGONWIDTH-self.XTHICKNESS, y+self.SIDELENGTH - self.YTHICKNESS), + (x+self.HEXAGONWIDTH/2, y+1.5*self.SIDELENGTH - self.HEXTHICKNESS), + (x+self.XTHICKNESS, y+self.SIDELENGTH - self.YTHICKNESS), + ],fill = self.BLANKCOLOR) + + # Draw color on the outside of the board + # Top line, red + d.line(sum((sum([(point[0],point[1],point[0]+self.HEXAGONWIDTH/2,point[1]-self.HEXAGONHEIGHT+self.SIDELENGTH) for point in self.BOARDCOORDINATES[0]],()),(self.BOARDCOORDINATES[0][10][0]+self.HEXAGONWIDTH*3/4,self.BOARDCOORDINATES[0][10][1]-self.SIDELENGTH/4)),()), + fill = self.PIECECOLOR[1],width = self.LINETHICKNESS) + # Bottom line, red + d.line(sum(((self.BOARDCOORDINATES[10][0][0]+self.HEXAGONWIDTH/4,self.BOARDCOORDINATES[10][0][1]+self.SIDELENGTH*5/4),sum([(point[0]+self.HEXAGONWIDTH/2,point[1]+self.HEXAGONHEIGHT,point[0]+self.HEXAGONWIDTH,point[1]+self.SIDELENGTH) for point in self.BOARDCOORDINATES[10]],())),()), + fill = self.PIECECOLOR[1],width = self.LINETHICKNESS) + # Left line, blue + d.line(sum((sum([(row[0][0],row[0][1],row[0][0],row[0][1]+self.SIDELENGTH) for row in self.BOARDCOORDINATES],()),(self.BOARDCOORDINATES[10][0][0]+self.HEXAGONWIDTH/4,self.BOARDCOORDINATES[10][0][1]+self.SIDELENGTH*5/4)),()), + fill = self.PIECECOLOR[2],width = self.LINETHICKNESS) + # Right line, blue + d.line(sum(((self.BOARDCOORDINATES[0][10][0]+self.HEXAGONWIDTH*3/4,self.BOARDCOORDINATES[0][10][1]-self.SIDELENGTH/4),sum([(row[10][0]+self.HEXAGONWIDTH,row[10][1],row[10][0]+self.HEXAGONWIDTH,row[10][1]+self.SIDELENGTH) for row in self.BOARDCOORDINATES],())),()), + fill = self.PIECECOLOR[2],width = self.LINETHICKNESS) + + # Writes "abc..", "123.." on the columns and rows + for i in range(11): + # Top letters + d.text( (self.XOFFSET + self.HEXAGONWIDTH*i, self.YOFFSET-66) , "ABCDEFGHIJK"[i], font=self.FONT, fill=self.TEXTCOLOR) + # Bottom letters + d.text( (self.XOFFSET + self.HEXAGONWIDTH*(i+11.5/2), self.YOFFSET - 15 + 11*self.HEXAGONHEIGHT) , "ABCDEFGHIJK"[i], font=self.FONT, fill=self.TEXTCOLOR) + # Left numbers + d.multiline_text( (self.XOFFSET + self.HEXAGONWIDTH*i/2 - 72 -4*(i>8), self.YOFFSET + 18 + i*self.HEXAGONHEIGHT) , str(i+1), font=self.FONT, fill=self.TEXTCOLOR, align="right") + # Right numbers + d.text( (self.XOFFSET + self.HEXAGONWIDTH*(i/2+11) + 30 , self.YOFFSET + 6 + i*self.HEXAGONHEIGHT) , str(i+1), font=self.FONT, fill=self.TEXTCOLOR) + + # Write player names and color + game = self.bot.database["hex games"].find_one({"_id":channel}) + + for p in [1,2]: + playername = self.bot.databaseFuncs.getName(game["players"][p-1]) + # Draw name + x = self.XNAME[p] + x -= self.NAMEFONT.getsize(playername)[0] if p==2 else 0 # player2's name is right-aligned + y = self.YNAME[p] + d.text((x,y),playername, font=self.NAMEFONT, fill = self.TEXTCOLOR) + # Draw a half-size Hexagon to indicate the player's color + x -= self.NAMEHEXPADDING # To the left of both names + d.polygon([ + (x, y), + (x+self.SMALLWIDTH/2, y-self.SMALLSIDELENGTH/2), + (x+self.SMALLWIDTH, y), + (x+self.SMALLWIDTH, y+self.SMALLSIDELENGTH), + (x+self.SMALLWIDTH/2, y+self.SMALLSIDELENGTH*3/2), + (x, y+self.SMALLSIDELENGTH), + ],fill = self.PIECECOLOR[p]) + + im.save("resources/games/hexBoards/board"+channel+".png") + + + + + def drawHexPlacement(self, channel,player,position): + FILEPATH = "resources/games/hexBoards/board"+channel+".png" + self.bot.log(f"Drawing a newly placed hex. Filename: board{channel}.png") + + # Translates 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 + + # Find the coordinates for the filled hex drawing + hexCoords = [ + (self.BOARDCOORDINATES[row][column][0]+self.COLXTHICKNESS, self.BOARDCOORDINATES[row][column][1] + self.COLYTHICKNESS), + (self.BOARDCOORDINATES[row][column][0]+self.HEXAGONWIDTH/2, self.BOARDCOORDINATES[row][column][1]-0.5*self.SIDELENGTH + self.COLHEXTHICKNESS), + (self.BOARDCOORDINATES[row][column][0]+self.HEXAGONWIDTH-self.COLXTHICKNESS, self.BOARDCOORDINATES[row][column][1] + self.COLYTHICKNESS), + (self.BOARDCOORDINATES[row][column][0]+self.HEXAGONWIDTH-self.COLXTHICKNESS, self.BOARDCOORDINATES[row][column][1]+self.SIDELENGTH - self.COLYTHICKNESS), + (self.BOARDCOORDINATES[row][column][0]+self.HEXAGONWIDTH/2, self.BOARDCOORDINATES[row][column][1]+1.5*self.SIDELENGTH - self.COLHEXTHICKNESS), + (self.BOARDCOORDINATES[row][column][0]+self.COLXTHICKNESS, self.BOARDCOORDINATES[row][column][1]+self.SIDELENGTH - self.COLYTHICKNESS), + ] + + # Opens the image + try: + with Image.open(FILEPATH) as im: + d = ImageDraw.Draw(im,"RGBA") + # Draws the hex piece + d.polygon(hexCoords,fill = self.PIECECOLOR[player], outline = self.BETWEENCOLOR) + + # Save + im.save(FILEPATH) + except: + self.bot.log("Error drawing new hex on board (error code 1541") + + def drawSwap(self, channel): + FILEPATH = "resources/games/hexBoards/board"+channel+".png" + game = self.bot.database["hex games"].find_one({"_id":channel}) + # Opens the image + try: + with Image.open(FILEPATH) as im: + d = ImageDraw.Draw(im,"RGBA") + + # Write player names and color + for p in [1,2]: + playername = self.bot.databaseFuncs.getName(game["players"][p%2]) + + x = self.XNAME[p] + x -= self.NAMEFONT.getsize(playername)[0] if p==2 else 0 # player2's name is right-aligned + y = self.YNAME[p] + + # Draw a half-size Hexagon to indicate the player's color + x -= self.NAMEHEXPADDING # To the left of both names + d.polygon([ + (x, y), + (x+self.SMALLWIDTH/2, y-self.SMALLSIDELENGTH/2), + (x+self.SMALLWIDTH, y), + (x+self.SMALLWIDTH, y+self.SMALLSIDELENGTH), + (x+self.SMALLWIDTH/2, y+self.SMALLSIDELENGTH*3/2), + (x, y+self.SMALLSIDELENGTH), + ],fill = self.PIECECOLOR[p % 2 + 1]) + + # Save + im.save(FILEPATH) + except: + self.bot.log("Error drawing swap (error code 1542)") diff --git a/funcs/games/hexDraw.py b/funcs/games/hexDraw.py deleted file mode 100644 index 8dcd7d0..0000000 --- a/funcs/games/hexDraw.py +++ /dev/null @@ -1,185 +0,0 @@ -import math - -from PIL import Image, ImageDraw, ImageFont - -# Defining all the variables -CANVAS_WIDTH = 2400 -CANVAS_HEIGHT = 1800 -SIDELENGTH = 75 -X_OFFSET = CANVAS_WIDTH/2 - 8*math.sqrt(3)*SIDELENGTH # The offsets centers the board in the picture -Y_OFFSET = CANVAS_HEIGHT/2 - 8*SIDELENGTH # The offsets are the coordinates of the upperleft point in the upperleftmost hexagon -HEXAGONWIDTH = math.sqrt(3) * SIDELENGTH # the whole width of one hexagon -HEXAGONHEIGHT = 1.5 * SIDELENGTH # not really the entire height, but the height difference between two layers -FONTSIZE = 45 -TEXTCOLOR = (0,0,0) -fnt = ImageFont.truetype('resources/fonts/futura-bold.ttf', FONTSIZE) - -LINETHICKNESS = 15 -HEXTHICKNESS = 6 # This is half the width of the background lining between every hex -X_THICKNESS = HEXTHICKNESS * math.cos(math.pi/6) -Y_THICKNESS = HEXTHICKNESS * math.sin(math.pi/6) -BACKGROUND_COLOR = (230,230,230) -BETWEEN_COLOR = (231,231,231) -BLANK_COLOR = "lightgrey" -PIECECOLOR = {1:(237,41,57),2:(0,165,255),0:BLANK_COLOR} # player1 is red, player2 is blue -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 # When placing a hex, it is a little bigger than the underlying hex in the background (4 < 6) -COLX_THICKNESS = COLHEXTHICKNESS * math.cos(math.pi/6) -COLY_THICKNESS = COLHEXTHICKNESS * math.sin(math.pi/6) -# The Name display things: -NAMESIZE = 60 -NAME_fnt = ImageFont.truetype('resources/fonts/futura-bold.ttf', NAMESIZE) -X_NAME = {1:175, 2:CANVAS_WIDTH-100} -Y_NAME = {1:CANVAS_HEIGHT-150, 2:150} -NAMEHEXPADDING = 90 -SMOL_WIDTH = HEXAGONWIDTH * 0.6 -SMOL_SIDELENGTH = SIDELENGTH * 0.6 - -class DrawHex(): - def __init__(self,bot): - self.bot = bot - - def drawBoard(self, channel): - self.bot.log("Drawing empty Hex board") - - # Creates the empty image - im = Image.new('RGB', size=(CANVAS_WIDTH, CANVAS_HEIGHT),color = BACKGROUND_COLOR) - # 'd' is a shortcut to drawing on the image - d = ImageDraw.Draw(im,"RGBA") - - # Drawing all the hexagons - for column in BOARDCOORDINATES: - for startingPoint in column: - x = startingPoint[0] - y = startingPoint[1] - d.polygon([ - (x, y), - (x+HEXAGONWIDTH/2, y-0.5*SIDELENGTH), - (x+HEXAGONWIDTH, y), - (x+HEXAGONWIDTH, y+SIDELENGTH), - (x+HEXAGONWIDTH/2, y+1.5*SIDELENGTH), - (x, y+SIDELENGTH), - ],fill = BETWEEN_COLOR) - d.polygon([ - (x+X_THICKNESS, y + Y_THICKNESS), - (x+HEXAGONWIDTH/2, y-0.5*SIDELENGTH + HEXTHICKNESS), - (x+HEXAGONWIDTH-X_THICKNESS, y + Y_THICKNESS), - (x+HEXAGONWIDTH-X_THICKNESS, y+SIDELENGTH - Y_THICKNESS), - (x+HEXAGONWIDTH/2, y+1.5*SIDELENGTH - HEXTHICKNESS), - (x+X_THICKNESS, y+SIDELENGTH - Y_THICKNESS), - ],fill = BLANK_COLOR) - - # Draw color on the outside of the board - # Top line, red - d.line(sum((sum([(point[0],point[1],point[0]+HEXAGONWIDTH/2,point[1]-HEXAGONHEIGHT+SIDELENGTH) for point in BOARDCOORDINATES[0]],()),(BOARDCOORDINATES[0][10][0]+HEXAGONWIDTH*3/4,BOARDCOORDINATES[0][10][1]-SIDELENGTH/4)),()), - fill = PIECECOLOR[1],width = LINETHICKNESS) - # Bottom line, red - d.line(sum(((BOARDCOORDINATES[10][0][0]+HEXAGONWIDTH/4,BOARDCOORDINATES[10][0][1]+SIDELENGTH*5/4),sum([(point[0]+HEXAGONWIDTH/2,point[1]+HEXAGONHEIGHT,point[0]+HEXAGONWIDTH,point[1]+SIDELENGTH) for point in BOARDCOORDINATES[10]],())),()), - fill = PIECECOLOR[1],width = LINETHICKNESS) - # Left line, blue - d.line(sum((sum([(row[0][0],row[0][1],row[0][0],row[0][1]+SIDELENGTH) for row in BOARDCOORDINATES],()),(BOARDCOORDINATES[10][0][0]+HEXAGONWIDTH/4,BOARDCOORDINATES[10][0][1]+SIDELENGTH*5/4)),()), - fill = PIECECOLOR[2],width = LINETHICKNESS) - # Right line, blue - d.line(sum(((BOARDCOORDINATES[0][10][0]+HEXAGONWIDTH*3/4,BOARDCOORDINATES[0][10][1]-SIDELENGTH/4),sum([(row[10][0]+HEXAGONWIDTH,row[10][1],row[10][0]+HEXAGONWIDTH,row[10][1]+SIDELENGTH) for row in BOARDCOORDINATES],())),()), - fill = PIECECOLOR[2],width = LINETHICKNESS) - - # Writes "abc..", "123.." on the columns and rows - for i in range(11): - # Top letters - d.text( (X_OFFSET + HEXAGONWIDTH*i, Y_OFFSET-66) , "ABCDEFGHIJK"[i], font=fnt, fill=TEXTCOLOR) - # Bottom letters - d.text( (X_OFFSET + HEXAGONWIDTH*(i+11.5/2), Y_OFFSET - 15 + 11*HEXAGONHEIGHT) , "ABCDEFGHIJK"[i], font=fnt, fill=TEXTCOLOR) - # Left numbers - d.multiline_text( (X_OFFSET + HEXAGONWIDTH*i/2 - 72 -4*(i>8), Y_OFFSET + 18 + i*HEXAGONHEIGHT) , str(i+1), font=fnt, fill=TEXTCOLOR, align="right") - # Right numbers - d.text( (X_OFFSET + HEXAGONWIDTH*(i/2+11) + 30 , Y_OFFSET + 6 + i*HEXAGONHEIGHT) , str(i+1), font=fnt, fill=TEXTCOLOR) - - # Write player names and color - game = self.bot.database["hex games"].find_one({"_id":channel}) - - for p in [1,2]: - playername = self.bot.databaseFuncs.getName(game["players"][p-1]) - # Draw name - x = X_NAME[p] - x -= NAME_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=NAME_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+SMOL_WIDTH/2, y-SMOL_SIDELENGTH/2), - (x+SMOL_WIDTH, y), - (x+SMOL_WIDTH, y+SMOL_SIDELENGTH), - (x+SMOL_WIDTH/2, y+SMOL_SIDELENGTH*3/2), - (x, y+SMOL_SIDELENGTH), - ],fill = PIECECOLOR[p]) - - im.save("resources/games/hexBoards/board"+channel+".png") - - - - - def drawHexPlacement(self, channel,player,position): - FILEPATH = "resources/games/hexBoards/board"+channel+".png" - self.bot.log(f"Drawing a newly placed hex. Filename: board{channel}.png") - - # Translates 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 - - # Find the coordinates for the filled hex drawing - hexCoords = [ - (BOARDCOORDINATES[row][column][0]+COLX_THICKNESS, BOARDCOORDINATES[row][column][1] + COLY_THICKNESS), - (BOARDCOORDINATES[row][column][0]+HEXAGONWIDTH/2, BOARDCOORDINATES[row][column][1]-0.5*SIDELENGTH + COLHEXTHICKNESS), - (BOARDCOORDINATES[row][column][0]+HEXAGONWIDTH-COLX_THICKNESS, BOARDCOORDINATES[row][column][1] + COLY_THICKNESS), - (BOARDCOORDINATES[row][column][0]+HEXAGONWIDTH-COLX_THICKNESS, BOARDCOORDINATES[row][column][1]+SIDELENGTH - COLY_THICKNESS), - (BOARDCOORDINATES[row][column][0]+HEXAGONWIDTH/2, BOARDCOORDINATES[row][column][1]+1.5*SIDELENGTH - COLHEXTHICKNESS), - (BOARDCOORDINATES[row][column][0]+COLX_THICKNESS, BOARDCOORDINATES[row][column][1]+SIDELENGTH - COLY_THICKNESS), - ] - - # Opens the image - try: - with Image.open(FILEPATH) as im: - d = ImageDraw.Draw(im,"RGBA") - # Draws the hex piece - d.polygon(hexCoords,fill = PIECECOLOR[player], outline = BETWEEN_COLOR) - - # Save - im.save(FILEPATH) - except: - self.bot.log("Error drawing new hex on board (error code 1541") - - def drawSwap(self, channel): - FILEPATH = "resources/games/hexBoards/board"+channel+".png" - game = self.bot.database["hex games"].find_one({"_id":channel}) - # Opens the image - try: - with Image.open(FILEPATH) as im: - d = ImageDraw.Draw(im,"RGBA") - - # Write player names and color - for p in [1,2]: - playername = self.bot.databaseFuncs.getName(game["players"][p%2]) - - x = X_NAME[p] - x -= NAME_fnt.getsize(playername)[0] if p==2 else 0 # player2's name is right-aligned - y = Y_NAME[p] - - # Draw a half-size Hexagon to indicate the player's color - x -= NAMEHEXPADDING # To the left of both names - d.polygon([ - (x, y), - (x+SMOL_WIDTH/2, y-SMOL_SIDELENGTH/2), - (x+SMOL_WIDTH, y), - (x+SMOL_WIDTH, y+SMOL_SIDELENGTH), - (x+SMOL_WIDTH/2, y+SMOL_SIDELENGTH*3/2), - (x, y+SMOL_SIDELENGTH), - ],fill = PIECECOLOR[p % 2 + 1]) - - # Save - im.save(FILEPATH) - except: - self.bot.log("Error drawing swap (error code 1542)")