from PIL import Image, ImageDraw, ImageFont import math as maths import logging import pygame import gameUtils import NoPELib import random import string import json import time import os alphabet = string.ascii_letters + string.digits + string.punctuation def normalDist(difficulty): """ This function generates a random number between 0-1 that should be used to get a font with a difficulty equal to or less than the number returned. """ # Create a random number between 0-1 v = random.randint(-1000, 1000) / 1000 # Redistribute the number v = v**3 # Shift the number based on game difficulty difficulty = difficulty * 1.5 - 0.25 v += difficulty # Cap it to be 0-1 again and return return min(max(v, 0), 1) def PIL2PG(pilImage: Image) -> pygame.Surface: """ Converts a PIL image to a Pygame surface. """ return pygame.image.fromstring(pilImage.tobytes(), pilImage.size, pilImage.mode) class Font: def __init__(self, data, fontSize): self.font = ImageFont.truetype(os.path.join('./fonts/', data['file']), fontSize) self.alphabet = data['alphabet'] self.difficulty = data['difficulty'] class Captcha: """ Holds and manages the current captcha. """ def __init__(self): self.chars = [] self.fonts = [] self.charIdx = 0 # Holds which character the player is on, # anthing below this index should appear green. def addChar(self, char, font): self.chars.append(char.upper()) self.fonts.append(font) def match(self, char): matches = char.upper() == self.chars[self.charIdx] print(f'{char.upper()} == {self.chars[self.charIdx]}') if matches: self.charIdx += 1 return True else: return False class Game(gameUtils.Game): def __init__(self, *args, **kwargs): """ Ran when the game starts. You have access to the following variables: self.surf (pygame.Surface): This is the surface you draw onto. self.pm (NoPELib.PlayerManager): This is where your players are stored. self.cfg (dict): Everything from the `game.toml` file. You can access it like this: self.details['title'] """ # Don't remove this. It does important things. :3 super().__init__(*args, **kwargs) # A value between 0-1 that defines which fonts the game will use self.gameDifficulty = 0 # The actual text in the captcha, used to check what the user typed self.currentCaptcha = None # The image of the captcha self.currentCaptchaImg = pygame.Surface((0, 0)) self.fonts = [] self.Drawsurface = Image.new("RGB",(800,220),(255,255,255)) # I haven't finished this yet, you might want to create # an object or something, I don't know. Keep in mind you # have to load all of the font files for use later. with open(f'./fonts/fonts.json', 'r') as f: for fontData in json.loads(f.read()): self.fonts.append(Font(fontData, 100)) #self.fontList = json.loads(f.read())#loads the fonts from the json (Why the fuck was the variable called 'j' before????) #for fontData in j: # self.fonts.update() def fontsByDifficulty(self, difficulty, width=0.3): """ Gets a list of fonts that fall within the specified difficulty. """ return [ font for font in self.fonts if font.difficulty >= difficulty-width and font.difficulty <= difficulty+width ] def createCaptcha(self): """ Use PIL (https://pillow.readthedocs.io/en/stable/handbook/tutorial.html) to draw the fonts, add noise, random shapes, transforms etc. based on self.gameDifficulty """ # Create a surface for that captcha drawSurface = Image.new("RGB", (800,220), (255,255,255)) # Create a drawing object draw = ImageDraw.Draw(drawSurface) # Temporarily stores the current captcha text captcha = Captcha() # Get a random cpatcha length capLength = random.randint(3, 6) for i in range(capLength): # Calculate the base X position of this character # TODO: Make this better, I'm not too happy with my implementation here. x = self.size[0] / (capLength + 3) x *= i # Get the selected difficulty for this character charDiff = normalDist(self.gameDifficulty) # Pick a random font based on that difficulty font = random.choice(self.fontsByDifficulty(charDiff)) # Pick a random character char = random.choice(font.alphabet) # Draw the selected character, with the selected font draw.text((x, 10), char, font=font.font, fill=(0,0,0)) # Append the character to the captcha captcha.addChar(char, font) # Store the correct captcha text self.currentCaptcha = captcha # Store the created image self.currentCaptchaImg = PIL2PG(drawSurface) print(captcha) def onEvent(self, event): """ Ran when an event is fired. Args: event (pygame.Event): The event that was fired. """ if event.type == pygame.KEYDOWN: if event.key == pygame.K_F1: self.gameDifficulty -= 0.1 print(self.gameDifficulty) elif event.key == pygame.K_F2: self.gameDifficulty += 0.1 print(self.gameDifficulty) elif event.key == pygame.K_F3: self.createCaptcha() elif event.unicode != '' and event.unicode in alphabet: print(self.currentCaptcha.match(event.unicode)) def update(self): """ Ran once per frame, put your drawing code and any game logic that should be ran once per frame in here. """ self.surf.fill((0, 0, 0)) #sets the background colour self.surf.blit(gameUtils.centre(self.currentCaptchaImg, self.size)) #draws the text to center of the screen #TODO: actually scale the text, however, more work needs to be done on the actual function of the game first def close(self): """ Ran when the game closes. """ pass