From 91ef430eb64aa229e34e6446a5914209b3d35c37 Mon Sep 17 00:00:00 2001 From: Nikiroy78 <35032449+Nikiroy78@users.noreply.github.com> Date: Thu, 28 Jan 2021 20:17:14 +0300 Subject: [PATCH] Add files via upload --- README.md | 68 +-- SQLEasy.py | 192 +++++++++ Theory/scheme.png | Bin 0 -> 21794 bytes Theory/scheme_noBg.png | Bin 0 -> 20724 bytes corelib.py | 168 ++++++++ game.py | 951 +++++++++++++++++++++++++++++++++++++++++ gameDataBase.db | Bin 0 -> 118784 bytes icon.ico | Bin 0 -> 67646 bytes launcher.py | 210 +++++++++ launcher.ui | 421 ++++++++++++++++++ runCore.sh | 2 + testgame.py | 109 +++++ update.bat | 3 + 13 files changed, 2090 insertions(+), 34 deletions(-) create mode 100644 SQLEasy.py create mode 100644 Theory/scheme.png create mode 100644 Theory/scheme_noBg.png create mode 100644 corelib.py create mode 100644 game.py create mode 100644 gameDataBase.db create mode 100644 icon.ico create mode 100644 launcher.py create mode 100644 launcher.ui create mode 100644 runCore.sh create mode 100644 testgame.py create mode 100644 update.bat diff --git a/README.md b/README.md index 10a3436..20c40e1 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,34 @@ -# Version 0.9 Beta -Тестовая версия для GNU Linux и OS Windows (x64).
-Для запуска программы требуется **python 3.6+**!!!
-В релизных версиях есть файл для запуска с консолью (для отладки) и стандартный файл
-Код писался под **OS Windows x64**, однако, в теории код работает и с GNU Linux, однако, автор **не гарантирует** 100% работу под Linux. -## Советы для работы с ядром -**Не запускайте скомпилированное ядро!! это может привести к некорректной работе игры!!!**
-Ниже приведён пример конфигурационного файла ядра. -```json - { - "pythonRun": true, # Запуск ядра при помощи python - "pythonPath_win32": "python ", # Путь к python на OS Windows x64 - "pythonPath_unix": "python3 ", # Путь к python на GNU Linux - "max_responseTime": 15, # Время ожидания отклика ядра (сек) - "core_file": "gamecore.py", # Файл ядра - "log_enable": false # Включить логгирование - } -``` -## Методы ядра -Запросы принимаются через файл **gamestat.json** -```json -{ - "status": "method" # Метод - "request": [] # Тело запроса -} -``` -После выполнения запроса, ядро высылает ответ в файле **response.json** -```json -{ - "response": [] # Тело ответа - "response_randomID": [] # random ID -} -``` -Подробнее читайте в документации. +# Version 0.9 Beta +Тестовая версия для GNU Linux и OS Windows (x64).
+Для запуска программы требуется **python 3.6+**!!!
+В релизных версиях есть файл для запуска с консолью (для отладки) и стандартный файл
+Код писался под **OS Windows x64**, однако, в теории код работает и с GNU Linux, однако, автор **не гарантирует** 100% работу под Linux. +## Советы для работы с ядром +**Не запускайте скомпилированное ядро!! это может привести к некорректной работе игры!!!**
+Ниже приведён пример конфигурационного файла ядра. +```json + { + "pythonRun": true, # Запуск ядра при помощи python + "pythonPath_win32": "python ", # Путь к python на OS Windows x64 + "pythonPath_unix": "python3 ", # Путь к python на GNU Linux + "max_responseTime": 15, # Время ожидания отклика ядра (сек) + "core_file": "gamecore.py", # Файл ядра + "log_enable": false # Включить логгирование + } +``` +## Методы ядра +Запросы принимаются через файл **gamestat.json** +```json +{ + "status": "method" # Метод + "request": [] # Тело запроса +} +``` +После выполнения запроса, ядро высылает ответ в файле **response.json** +```json +{ + "response": [] # Тело ответа + "response_randomID": [] # random ID +} +``` +Подробнее читайте в документации. diff --git a/SQLEasy.py b/SQLEasy.py new file mode 100644 index 0000000..f349ecb --- /dev/null +++ b/SQLEasy.py @@ -0,0 +1,192 @@ +import sqlite3 + + +class SQLiteEasyException(Exception): + pass + + +def compareKey(DBlist, key, type_of_key=lambda x: x): + if not(type(DBlist) is list): + raise SQLiteEasyException(f"function compareKey need List object, unsupported type: {type(DBlist)}") + + if len(DBlist) > 0: + if key not in DBlist[0]: + raise SQLiteEasyException(f"key '{key}' not founded.") + DB_Dictonary = dict() + for BD in sorted(DBlist, key=lambda List: List[key]): + comparedBD = dict() + for Key in BD: + if not(Key == key): + comparedBD[Key] = BD[Key] + DB_Dictonary[type_of_key(BD[key])] = comparedBD + + return DB_Dictonary + + +class database: + def dict_factory(self, cursor, row): + dictDB = {} + for idx, col in enumerate(cursor.description): + dictDB[col[0]] = row[idx] + return dictDB + + def encodeSQLiteType(self, objectum, all_as_str=False): + if type(objectum) is str: + return "'%s'" % objectum.replace('\'', '\\\'') + elif objectum is None and all_as_str: + return "''" + elif objectum is None: + return 'NULL' + elif type(objectum) is bool and objectum and all_as_str: + return "'TRUE'" + elif type(objectum) is bool and objectum: + return 'TRUE' + elif type(objectum) is bool and not(objectum) and all_as_str: + return "'FALSE'" + elif type(objectum) is bool and not(objectum): + return 'FALSE' + elif all_as_str and True in [type(objectum) is int, type(objectum) is float]: + return "'%s'" % objectum + elif not(all_as_str) and True in [type(objectum) is int, type(objectum) is float]: + return objectum + else: + raise SQLiteEasyException(f"Unsupported type: {type(objectum)}") + + def __init__(self, PATH, DatabaseName=None): + self.ConnectedFile = sqlite3.connect(PATH) + self.databaseChoosed = DatabaseName + self.ConnectedFile.row_factory = self.dict_factory + self.act_commit = True + + def toggleCommit(self, value=None): + if value in (False, True): + self.act_commit = value + else: + if self.act_commit: + self.act_commit = False + else: + self.act_commit = True + + def commit(self): + self.ConnectedFile.commit() + + def getBase(self, DatabaseName=None, elementsFromDB='*'): + elementsFromDB = str(elementsFromDB) + if DatabaseName is None and self.databaseChoosed is None: + raise SQLiteEasyException("Database is not choosed") + elif DatabaseName is None and not(self.databaseChoosed is None): + dbCursore = self.ConnectedFile.cursor() + dbCursore.execute(f"select {elementsFromDB} from {self.databaseChoosed}") + return dbCursore.fetchall() + else: + dbCursore = self.ConnectedFile.cursor() + dbCursore.execute(f"select {elementsFromDB} from {DatabaseName}") + self.databaseChoosed = DatabaseName + return dbCursore.fetchall() + + def pop(self, key, value, DatabaseName=None): + if type(value) is str: + value = f"'%s'" % value.replace('\'', '\\\'') + if DatabaseName is None and not(self.databaseChoosed is None): + DatabaseName = self.databaseChoosed + elif DatabaseName is None: + raise SQLiteEasyException("Database is not choosed") + dbCursore = self.ConnectedFile.cursor() + dbCursore.execute('DELETE FROM %s\n\nWHERE %s == %s;' % (DatabaseName, key, value)) + if self.act_commit: + self.ConnectedFile.commit() + + def setItem(self, key, newValue, indexKey, value, DatabaseName=None): + newValue = self.encodeSQLiteType(newValue) + value = self.encodeSQLiteType(value) + if DatabaseName is None and not(self.databaseChoosed is None): + DatabaseName = self.databaseChoosed + elif DatabaseName is None: + raise SQLiteEasyException("Database is not choosed") + dbCursore = self.ConnectedFile.cursor() + if value is None: + dbCursore.execute('UPDATE %s SET %s = %s WHERE %s is NULL;' % (DatabaseName, key, newValue, indexKey)) + else: + dbCursore.execute('UPDATE %s SET %s = %s WHERE %s = %s;' % (DatabaseName, key, newValue, indexKey, value)) + if self.act_commit: + self.ConnectedFile.commit() + + def add(self, values, DatabaseName=None): + if DatabaseName is None and not(self.databaseChoosed is None): + DatabaseName = self.databaseChoosed + elif DatabaseName is None: + raise SQLiteEasyException("Database is not choosed") + if DatabaseName is None and not(self.databaseChoosed is None): + DatabaseName = self.databaseChoosed + elif DatabaseName is None: + raise SQLiteEasyException("Database is not choosed") + + keys = [str(key) for key in values] + values = [self.encodeSQLiteType(values[key], all_as_str=True) for key in values] + + dbCursore = self.ConnectedFile.cursor() + dbCursore.execute('INSERT INTO %s (%s) VALUES (%s)' % (DatabaseName, ', '.join(keys), ', '.join(values))) + if self.act_commit: + self.ConnectedFile.commit() + + def uploadFiles(self, binary, DatabaseName=None): # Uwaga! Может работать с ошибками. + if not(type(binary) is bytes or type(binary) is bytearray): + raise SQLiteEasyException('You can upload only byte or bytearray types!!') + dbCursore = self.ConnectedFile.cursor() + binary = sqlite3.Binary(binary) + + if DatabaseName is None and not(self.databaseChoosed is None): + DatabaseName = self.databaseChoosed + elif DatabaseName is None: + raise SQLiteEasyException("Database is not choosed") + if DatabaseName is None and not(self.databaseChoosed is None): + DatabaseName = self.databaseChoosed + elif DatabaseName is None: + raise SQLiteEasyException("Database is not choosed") + # (!) Дописать + + def chooseDataBase(self, DatabaseName): + self.getBase(DatabaseName) + self.databaseChoosed = DatabaseName + + def currentIndex(self, key, value, DatabaseName=None): + if DatabaseName is None and not(self.databaseChoosed is None): + DatabaseName = self.databaseChoosed + elif DatabaseName is None: + raise SQLiteEasyException("Database is not choosed") + + DB = self.getBase(DatabaseName) + + ID = 0 + for Dictonary in sorted(DB, key=lambda dictonary: dictonary[key]): + if Dictonary[key] == value: + return ID + ID += 1 + + def currentValue(self, key, value, DatabaseName=None): + if DatabaseName is None and not(self.databaseChoosed is None): + DatabaseName = self.databaseChoosed + elif DatabaseName is None: + raise SQLiteEasyException("Database is not choosed") + + DB = self.getBase(DatabaseName) + + for Dictonary in sorted(DB, key=lambda dictonary: dictonary[key]): + if Dictonary[key] == value: + return Dictonary + + def getTables(self): + dbCursore = self.ConnectedFile.cursor() + dbCursore.execute('SELECT name from sqlite_master where type= "table"') + return [item['name'] for item in dbCursore.fetchall()] + + def getDict(self): + dictonary = dict() + tables = self.getTables() + for table in tables: + dictonary[table] = self.getBase(table) + return dictonary + + +def formingTable(dictonary, path): + pass # В разработке \ No newline at end of file diff --git a/Theory/scheme.png b/Theory/scheme.png new file mode 100644 index 0000000000000000000000000000000000000000..fd7dc8062014e9dc6095a12956f948dee4bbb563 GIT binary patch literal 21794 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℘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 Fe*lCUF$4er literal 0 HcmV?d00001 diff --git a/corelib.py b/corelib.py new file mode 100644 index 0000000..3453c62 --- /dev/null +++ b/corelib.py @@ -0,0 +1,168 @@ +import os, sys, json + +import threading, Gamecore.guiDrawWaiting +from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5.QtWidgets import * +from PyQt5.Qt import * + +with open('Gamecore/config.json', 'r', encoding='utf-8') as cfg: + cfg = json.loads(cfg.read()) + +if cfg["pythonRun"]: + if sys.platform == 'win32': + runCoreApp = cfg["pythonPath_win32"] + else: + runCoreApp = cfg["pythonPath_unix"] +else: + if sys.platform == 'win32': + runCoreApp = 'start ' + else: + runCoreApp = './' + + +class tool: + def __init__(self): + def showGUI(): + class coreGUI(threading.Thread): + def run(self): + class app_win(QMainWindow): + def __init__(self): + super(app_win, self).__init__() + self.ui = Gamecore.guiDrawWaiting.Ui_Form() + self.ui.setupUi(self) + self.show() + + self.app = QApplication([]) + self.application = app_win() + self.app.exec() + + def stop(self): + self.application.hide() + del self + + thrObj = coreGUI() + thrObj.start() + return thrObj + + def noneGUI(*args, **kwargs): + pass + + def stopGUI(thrObj): + thrObj.stop() + + self.showGUI = showGUI + self.noneGUI = noneGUI + self.stopGUI = stopGUI + + self.wait = self.noneGUI + self.resp = self.noneGUI + + def showGUI_wait(self, show=True): + if show: + print('WARNING! You will be using GUI corelib!!!') + self.wait = self.showGUI + self.resp = self.stopGUI + else: + self.wait = self.noneGUI + self.resp = self.noneGUI + + def response(self, resp): + waitObj = self.wait() + + respf = open('Gamecore/gamestat.json', 'wt', encoding='utf-8') + respf.write(json.dumps(resp, indent="\t", ensure_ascii=False)) + respf.close() + + shellScript = open('runCore.sh', 'wt', encoding='utf-8') + print(f"cd Gamecore\n{runCoreApp}{cfg['core_file']}") + shellScript.write(f"cd Gamecore\n{runCoreApp}{cfg['core_file']}") + shellScript.close() + if sys.platform != 'win32': + os.system("./runCore.sh") + else: + os.system("runCore.sh") + + output = open('Gamecore/response.json', 'r', encoding='utf-8') + outp = output.read() + output.close() + + returnObj = json.loads(outp) + self.resp(waitObj) + return returnObj + + +tool = tool() + + +def newGame(ID, mines=25, fieldsize=10): + return tool.response({ + "status": "newGame", + "request": { + "ID": ID, + "Mines": mines, + "size": fieldsize + } + }) + + +def openItem(gamesession, X, Y): + return tool.response({ + "status": "openItem", + "request": { + "gamesession": gamesession, + "X": X, + "Y": Y + } + }) + + +def getGameSession(gamesession): + return tool.response({ + "status": "getGameSession", + "request": { + "gamesession": gamesession + } + }) + + +def toggleFlag(gamesession, X, Y): + return tool.response({ + "status": "toggleFlag", + "request": { + "gamesession": gamesession, + "X": X, + "Y": Y + } + }) + + +class Method: + def __init__(self, method, **kwargs): + self.method = method + self.kwargs = kwargs + + def start(self): + return eval(self.method)(**kwargs) + + +class multiMethod: + def __init__(self, *methods): + self.methods = list(methods) + + def append(self, method): + self.methods.append(method) + + def pop(self, index): + self.methods.pop(index) + + def __len__(self): + return len(self.methods) + + def start(self): + request = {"status": 'multiMethod', 'requests': list()} + for method in self.methods: + request['requests'].append({ + "status": method.method, + "request": method.kwargs + }) + return tool.response(request) \ No newline at end of file diff --git a/game.py b/game.py new file mode 100644 index 0000000..3af9e11 --- /dev/null +++ b/game.py @@ -0,0 +1,951 @@ +from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5.QtWidgets import * +from PyQt5.Qt import * +import SQLEasy, json, os, random, corelib +import launcher # Интерфейс + + +def game(gameStyle, databaseObj, gamerID, gamehash, muzicOn=True, MINES=random.randint(25, 55)): + MINES_COMPR = False + import pygame + + corelib.tool.showGUI_wait() + + databaseObj.setItem('lastGame', gameStyle, 'ID', gamerID, DatabaseName='users') + # Импорт конфига + with open(f"source/{gameStyle}/about.json", 'r', encoding='utf-8') as gameconfig: + gameconfig = json.loads(gameconfig.read()) + # Функции + + + def checkEnd(field): + endgame = True + for r in field: + for c in r: + if c in ('M:O', 'M', 'M:F'): + return True + if 'R' in c and ':O' not in c: + endgame = False + return endgame + + + def checkWin(field, endgamecheck=False): + fieldCheck = list() + endgame = True + for r in field: + for c in r: + fieldCheck.append(c) + if 'R' in c and ':O' not in c: + endgame = False + if endgamecheck: + return 'M:O' not in fieldCheck and endgame + else: + return 'M:O' not in fieldCheck + + + def compareFields(old_field, field): + if checkWin(field, True): + return + + class emptySound: + def play(self): + pass + + sounds = [ + emptySound(), + pygame.mixer.Sound(f"source/{gameStyle}/openCell.wav"), + pygame.mixer.Sound(f"source/{gameStyle}/flag.wav") + ] + + actID = 0 + + comlete = False + + for rowID in range(len(field)): + for cellID in range(len(field[rowID])): + if field[rowID][cellID] != old_field[rowID][cellID]: + if ':F' in field[rowID][cellID]: + actID = 2 + comlete = True + break + elif ':O' in field[rowID][cellID] and field[rowID][cellID] != 'M:O': + actID = 1 + comlete = True + break + if comlete: + break + + sounds[actID].play() + # Классы игровых объектов + + + class controlBanner(pygame.sprite.Sprite): + def __init__(self, X, Y, platform='XBox', info_contentID=0): + infocontent = ['openCell', 'flagCell', 'restart'] + pygame.sprite.Sprite.__init__(self) + self.image = pygame.Surface((228, 77)) + self.platform = platform + self.info_contentID = info_contentID + self.image = pygame.image.load(f"source/{gameStyle}/{platform}_{infocontent[info_contentID]}.png") + self.rect = self.image.get_rect() + self.rect.x = X + self.rect.y = Y + + def changePlatform(self, platform): + infocontent = ['openCell', 'flagCell', 'restart'] + self.platform = platform + self.image = pygame.image.load(f"source/{gameStyle}/{platform}_{infocontent[self.info_contentID]}.png") + + + class Border(pygame.sprite.Sprite): + def __init__(self, X, Y, borderType='Left'): + pygame.sprite.Sprite.__init__(self) + self.image = pygame.Surface((228, 77)) + self.animationFrameTime = 0 + self.borderType = borderType + self.image = pygame.image.load(f"source/{gameStyle}/score_end{borderType}(0).png") + self.rect = self.image.get_rect() + self.rect.x = X + self.rect.y = Y + + def update(self): + self.animationFrameTime += 1 + if self.animationFrameTime >= 45: + self.animationFrameTime = 0 + self.image = pygame.image.load(f"source/{gameStyle}/score_end{self.borderType}({self.animationFrameTime // 15}).png") + + + class resetButton(pygame.sprite.Sprite): + def __init__(self, X, Y): + pygame.sprite.Sprite.__init__(self) + self.image = pygame.Surface((228, 77)) + self.hide = 'noHover' + self.endStatus = '' + self.image = pygame.image.load(f"source/{gameStyle}/resetButton_{self.endStatus}_{self.hide}.png") + self.rect = self.image.get_rect() + self.x = X + self.y = Y + + self.rect.x = X + self.rect.y = Y + + def editCond(self, hide=None, endStatus=None): + if not(hide is None): + self.hide = hide + if not(endStatus is None): + self.endStatus = endStatus + + self.image = pygame.image.load(f"source/{gameStyle}/resetButton_{self.endStatus}_{self.hide}.png") + + def check_myself_Location(self, X, Y): + if X >= self.x and X <= self.x + 32 and Y >= self.y and Y <= self.y + 32: + return self + + + class timerCell(pygame.sprite.Sprite): + def __init__(self, X, Y, value='off'): + pygame.sprite.Sprite.__init__(self) + self.image = pygame.Surface((228, 77)) + self.value = value + self.image = pygame.image.load(f"source/{gameStyle}/score_{value}.png") + self.rect = self.image.get_rect() + self.rect.x = X + self.rect.y = Y + + def editValue(self, value='off'): + self.value = value + self.image = pygame.image.load(f"source/{gameStyle}/score_{self.value}.png") + + def update(self): + self.image = pygame.image.load(f"source/{gameStyle}/score_{self.value}.png") + + + class tablet(pygame.sprite.Sprite): + def __init__(self, X, Y): + pygame.sprite.Sprite.__init__(self) + self.image = pygame.Surface((228, 77)) + self.image = pygame.image.load(f"source/{gameStyle}/Tablet.png") + self.rect = self.image.get_rect() + self.rect.x = X + self.rect.y = Y + + + class fieldCursor(pygame.sprite.Sprite): + def __init__(self, X, Y): + pygame.sprite.Sprite.__init__(self) + self.image = pygame.Surface((32, 32)) + self.image = pygame.image.load(f"source/{gameStyle}/Cursor.png") + self.cellPos = (X, Y) + self.rect = self.image.get_rect() + self.rect.x = 93 + X * 31 + self.rect.y = 133 + Y * 31 + + def setCell_coord(self, X, Y): + self.rect.x = 93 + X * 31 + self.rect.y = 133 + Y * 31 + self.cellPos = (X, Y) + + + class cell(pygame.sprite.Sprite): + def __init__(self, X, Y, coords, condition='CLOSED'): + pygame.sprite.Sprite.__init__(self) + self.image = pygame.Surface((32, 32)) + self.image = pygame.image.load(f"source/{gameStyle}/closed_cell.png") + self.rect = self.image.get_rect() + self.rect.x = X + self.rect.y = Y + self.cellPosition = { + "X": coords[0], + "Y": coords[1] + } + + self.condition = condition + self.animationFrameS = 0 + + def getCondition(self): + return self.condition + + def update(self): + if self.condition == 'CLOSED': + self.image.blit(pygame.image.load(f"source/{gameStyle}/closed_cell.png"), (0, 0)) + elif 'num_' in self.condition: + number = int(self.condition.split('_')[-1]) + self.animationFrameS += 1 + if self.animationFrameS >= 40: + self.animationFrameS = 0 + self.image.blit(pygame.image.load(f"source/{gameStyle}/mines_{number}({self.animationFrameS // 20}).png"), (0, 0)) + elif self.condition == 'OPENED': + self.animationFrameS += 1 + if self.animationFrameS >= 40: + self.animationFrameS = 0 + self.image.blit(pygame.image.load(f"source/{gameStyle}/open_cell_{self.animationFrameS // 20}.png"), (0, 0)) + elif self.condition == 'FLAG': + self.animationFrameS += 1 + if self.animationFrameS >= 40: + self.animationFrameS = 0 + self.image.blit(pygame.image.load(f"source/{gameStyle}/flag_{self.animationFrameS // 20}.png"), (0, 0)) + elif self.condition == 'BOMB': + self.animationFrameS += 1 + if self.animationFrameS >= 40: + self.animationFrameS = 0 + self.image.blit(pygame.image.load(f"source/{gameStyle}/mine_bombed({self.animationFrameS // 20}).png"), (0, 0)) + elif self.condition == 'DEFUSE': + self.animationFrameS += 1 + if self.animationFrameS >= 40: + self.animationFrameS = 0 + self.image.blit(pygame.image.load(f"source/{gameStyle}/mine_finded({self.animationFrameS // 20}).png"), (0, 0)) + + def setCondition(self, value): + self.condition = value + + + class sceneGame(pygame.sprite.Group): + def __init__(self, platform): + pygame.sprite.Group.__init__(self) + # Sounds + self.bombSound = pygame.mixer.Sound(f"source/{gameStyle}/bomb.wav") + self.winSound = pygame.mixer.Sound(f"source/{gameStyle}/win.wav") + + self.soundNotPlayed = True + + self.PLATFORM = platform + self.cells = list() + for row in range(10): + self.cells.append(list()) + for _cell in range(10): + addCell = cell(93 + _cell * 31, 133 + row * 31, (_cell, row)) + self.add(addCell) + self.cells[-1].append({ + "cellObject": addCell, + "coord_start": [93 + _cell * 31, 133 + row * 31], + "coord_last": [93 + (_cell + 1) * 31, 133 + (row + 1) * 31], + "coords_in_field": (_cell, row) + }) + self.add(tablet(130, 14)) + self.positionCursor = (0, 0) + self.cursor = fieldCursor(*self.positionCursor) + self.add(self.cursor) + self.add(Border(166, 93)) + self.timeCells = list() + for i in range(3): + timerCellObj = timerCell(198 + i * 32, 93) + self.add(timerCellObj) + self.timeCells.append(timerCellObj) + self.add(Border(294, 93, 'Right')) + self.resetButton = resetButton(228, 448) + self.add(self.resetButton) + + self.InfoTables = list() + + for i in range(3): + control_banner = controlBanner(0, 404 + i * 32, platform, i) + self.add(control_banner) + self.InfoTables.append(control_banner) + + def changePlatform(self, PLATFORM): + self.PLATFORM = PLATFORM + for tableObj in self.InfoTables: + tableObj.changePlatform(PLATFORM) + + def setScoreBoard_value(self, value=None): + if not(value is None): + value = str(value) + if len(value) == 1: + value = '00' + value + elif len(value) == 2: + value = '0' + value + + for numID in range(len(value)): + self.timeCells[numID].editValue(int(value[numID])) + else: + for cellT in self.timeCells: + cellT.editValue() + + def getCell_intoCoords(self, x, y): + for row in self.cells: + for _cell in row: + if (x >= _cell["coord_start"][0] and x <= _cell["coord_last"][0]) and (y >= _cell["coord_start"][1] and y <= _cell["coord_last"][1]): + return _cell["cellObject"] + + def cursorMove(self, x, y): + self.positionCursor = (x, y) + self.cursor.setCell_coord(x, y) + + def get_cursorPos(self): + return list(self.positionCursor) + + def updateField(self, field, gamefinished=None): + global MINES_COMPR + + listcells = list() + for r in field: + for c in r: + listcells.append(c) + if gamefinished is None: + gamefinished = 'M:O' in listcells + + for rowId in range(len(field)): + row = field[rowId] + for cellID in range(len(field[rowId])): + _cell = field[rowId][cellID] + + actCell = self.cells[rowId][cellID]["cellObject"] + if ':O' in _cell: + if not(gamefinished): + gamefinished = _cell == 'M:O' + + if _cell == 'M:O': + actCell.setCondition('BOMB') + else: + SeeUp = rowId != 0 + SeeDown = rowId != len(field) - 1 + SeeLeft = cellID != 0 + SeeRight = cellID != len(field) - 1 + + mines = 0 + frontire = False + if SeeUp: + if field[rowId - 1][cellID] in ('M', 'M:F', 'M:O'): + mines += 1 + if not(frontire): + frontire = _cell != field[rowId - 1][cellID] and ':O' not in field[rowId - 1][cellID] + if SeeDown: + if field[rowId + 1][cellID] in ('M', 'M:F', 'M:O'): + mines += 1 + if not(frontire): + frontire = _cell != field[rowId + 1][cellID] and ':O' not in field[rowId + 1][cellID] + if SeeLeft: + if field[rowId][cellID - 1] in ('M', 'M:F', 'M:O'): + mines += 1 + if not(frontire): + frontire = _cell != field[rowId][cellID - 1] and ':O' not in field[rowId][cellID - 1] + if SeeRight: + if field[rowId][cellID + 1] in ('M', 'M:F', 'M:O'): + mines += 1 + if not(frontire): + frontire = _cell != field[rowId][cellID + 1] and ':O' not in field[rowId][cellID + 1] + # Диагонали + if SeeUp and SeeLeft: + if field[rowId - 1][cellID - 1] in ('M', 'M:F', 'M:O'): + mines += 1 + if SeeUp and SeeRight: + if field[rowId - 1][cellID + 1] in ('M', 'M:F', 'M:O'): + mines += 1 + if SeeDown and SeeLeft: + if field[rowId + 1][cellID - 1] in ('M', 'M:F', 'M:O'): + mines += 1 + if SeeDown and SeeRight: + if field[rowId + 1][cellID + 1] in ('M', 'M:F', 'M:O'): + mines += 1 + + if mines > 0: + actCell.setCondition('num_%s' % mines) + elif frontire: + actCell.setCondition('num_1') + else: + actCell.setCondition('OPENED') + elif ':F' in _cell: + if gamefinished and _cell == 'M:F': + actCell.setCondition('DEFUSE') + else: + actCell.setCondition('FLAG') + else: + actCell.setCondition('CLOSED') + if not(gamefinished): + self.resetButton.editCond(endStatus='') + elif checkWin(field): + MINES_COMPR = True + self.resetButton.editCond(endStatus='win') + self.winSound.play() + minesDefused = 0 + scores = 0 + + for row in field: + for _cell in row: + if _cell == 'M:F': + minesDefused += 1 + + for row in field: + for _cell in row: + if _cell == 'M:F': + if (minesDefused // 5) ** 2 > 15: + scores += (minesDefused // 5) ** 2 > 10 + else: + scores += 15 + + databaseObj.setItem( + 'minesDefuse', + SQLEasy.compareKey(databaseObj.getBase('users'), 'ID')[gamerID]['minesDefuse'] + minesDefused, + 'ID', + gamerID, + DatabaseName='users' + ) + + databaseObj.setItem( + 'points', + SQLEasy.compareKey(databaseObj.getBase('users'), 'ID')[gamerID]['points'] + scores, + 'ID', + gamerID, + DatabaseName='users' + ) + else: + MINES_COMPR = True + self.resetButton.editCond(endStatus='lose') + self.bombSound.play() + + minesDefused = 0 + scores = 0 + + for row in field: + for _cell in row: + if _cell == 'M:F': + minesDefused += 1 + scores += 10 + + databaseObj.setItem( + 'minesDefuse', + SQLEasy.compareKey(databaseObj.getBase('users'), 'ID')[gamerID]['minesDefuse'] + minesDefused, + 'ID', + gamerID, + DatabaseName='users' + ) + + databaseObj.setItem( + 'points', + SQLEasy.compareKey(databaseObj.getBase('users'), 'ID')[gamerID]['points'] + scores, + 'ID', + gamerID, + DatabaseName='users' + ) + + + # код игры + logicGame = corelib.getGameSession(gamehash)['response'] + gamesession = logicGame["gamesession"] + field = logicGame["map"] + + if gamesession not in SQLEasy.compareKey(databaseObj.getBase('gamehashes'), 'hash'): + databaseObj.add({ + "hash": gamesession, + "userID": gamerID, + "mines": MINES, + "game": gameStyle, + "fieldJSON": json.dumps(field, indent="\t", ensure_ascii=False) + }, 'gamehashes') + + pygame.init() + + screen = pygame.display.set_mode((500, 500)) + + pygame.display.set_caption(gameconfig['title']) + pygame.display.set_icon(pygame.image.load(f"source/{gameStyle}/gameico.png")) + bg = pygame.image.load(f"source/{gameStyle}/bg.png") + screen.blit(bg, (0, 0)) + + PLATFORM = 'XBox' # (!) Сделать проверку + # Прорисовка полей + sceneGame = sceneGame(PLATFORM) + + pygame.display.flip() + + program_running = True + + cursor_moveUp = False + cursor_moveDown = False + cursor_moveLeft = False + cursor_moveRight = False + + # Muzic + pygame.mixer.music.load(f"source/{gameStyle}/muzic.wav") + if muzicOn: + pygame.mixer.music.play() + pygame.mixer.Sound(f"source/{gameStyle}/resetField.wav").play() + databaseObj.setItem( + 'games', + SQLEasy.compareKey(databaseObj.getBase('users'), 'ID')[gamerID]['games'] + 1, + 'ID', + gamerID, + DatabaseName='users' + ) + + sceneGame.updateField(field) + + while program_running: + pygame.time.Clock().tick(60) # max FPS + for event in pygame.event.get(): + if event.type == pygame.QUIT: + program_running = False + break + elif event.type == pygame.MOUSEMOTION: + POSITION = event.pos + cellSel = sceneGame.getCell_intoCoords(int(POSITION[0]), int(POSITION[1])) + + resetButtonSel = sceneGame.resetButton.check_myself_Location(int(POSITION[0]), int(POSITION[1])) + if not(cellSel is None): + sceneGame.cursorMove(cellSel.cellPosition["X"], cellSel.cellPosition["Y"]) + del POSITION, cellSel + if not(resetButtonSel is None): + resetButtonSel.editCond(hide='hover') + else: + sceneGame.resetButton.editCond(hide='noHover') + elif event.type == pygame.KEYDOWN: + cursor_moveUp = event.key == 1073741906 + cursor_moveDown = event.key == 1073741905 + cursor_moveLeft = event.key == 1073741904 + cursor_moveRight = event.key == 1073741903 + + cursor_coords = sceneGame.get_cursorPos() + if cursor_moveUp: + cursor_coords[1] -= 1 + if cursor_moveDown: + cursor_coords[1] += 1 + if cursor_moveLeft: + cursor_coords[0] -= 1 + if cursor_moveRight: + cursor_coords[0] += 1 + + if cursor_coords[0] <= -1: + cursor_coords[0] = 9 + if cursor_coords[0] >= 10: + cursor_coords[0] = 0 + if cursor_coords[1] <= -1: + cursor_coords[1] = 9 + if cursor_coords[1] >= 10: + cursor_coords[1] = 0 + + sceneGame.cursorMove(*cursor_coords) + elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: + POSITION = event.pos + cellSel = sceneGame.getCell_intoCoords(int(POSITION[0]), int(POSITION[1])) + + resetButtonSel = sceneGame.resetButton.check_myself_Location(int(POSITION[0]), int(POSITION[1])) + if not(cellSel is None): + corelib.openItem(gamesession, X=cellSel.cellPosition["X"], Y=cellSel.cellPosition["Y"]) + respObj = corelib.getGameSession(gamesession)['response'] + old_field = field + field = respObj['map'] + compareFields(old_field, field) + sceneGame.updateField(field, gamefinished=not(respObj['continue'])) + del POSITION, cellSel + if not(resetButtonSel is None): + pygame.mixer.Sound(f"source/{gameStyle}/resetField.wav").play() + MINES = random.randint(25, 55) + logicGame = corelib.newGame(gamerID, mines=MINES)['response'] + gamesession = logicGame["gamesession"] + sceneGame.updateField(logicGame["map"]) + if muzicOn: + pygame.mixer.music.play() + MINES_COMPR = False + + databaseObj.setItem( + 'games', + SQLEasy.compareKey(databaseObj.getBase('users'), 'ID')[gamerID]['games'] + 1, + 'ID', + gamerID, + DatabaseName='users') + + databaseObj.add({ + "hash": gamesession, + "userID": gamerID, + "game": gameStyle, + "mines": MINES, + "fieldJSON": json.dumps(field, indent="\t", ensure_ascii=False) + }, 'gamehashes') + elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 3: + POSITION = event.pos + cellSel = sceneGame.getCell_intoCoords(int(POSITION[0]), int(POSITION[1])) + if not(cellSel is None): + if MINES > 0 and cellSel.getCondition() == 'CLOSED' and not(MINES_COMPR): + if 'response' in corelib.toggleFlag(gamesession, X=cellSel.cellPosition["X"], Y=cellSel.cellPosition["Y"]): + MINES -= 1 + respObj = corelib.getGameSession(gamesession)['response'] + old_field = field + field = respObj['map'] + compareFields(old_field, field) + sceneGame.updateField(field, gamefinished=not(respObj['continue'])) + del POSITION, cellSel + elif cellSel.getCondition() == 'FLAG' and not(MINES_COMPR): + if 'response' in corelib.toggleFlag(gamesession, X=cellSel.cellPosition["X"], Y=cellSel.cellPosition["Y"]): + MINES += 1 + respObj = corelib.getGameSession(gamesession)['response'] + old_field = field + field = respObj['map'] + compareFields(old_field, field) + sceneGame.updateField(field, gamefinished=not(respObj['continue'])) + del POSITION, cellSel + databaseObj.setItem( + 'mines', + MINES, + 'hash', + gamesession, + DatabaseName='gamehashes' + ) + + sceneGame.setScoreBoard_value(MINES) + + sceneGame.draw(screen) + sceneGame.update() + pygame.display.flip() + + pygame.quit() + + +def getGamesDict(): + listGames = dict() + for directory in [f for f in os.listdir('source') if len(f.split('.')) == 1]: + if 'about.json' in os.listdir(f"source/{directory}"): + f = open(f"source/{directory}/about.json", 'r', encoding='utf-8') + content = json.loads(f.read()) + listGames[directory] = { + "gamename": content['title'], + "path": f"source/{directory}" + } + return listGames + + +def getGameName(name): + if name in getGamesDict(): + return getGamesDict()[name]["gamename"] + else: + return 'Game not founded.' + + +class app_win(QMainWindow): + def __init__(self): + super(app_win, self).__init__() + self.ui = launcher.Ui_Form() + self.ui.setupUi(self) + + self.database = SQLEasy.database('gameDataBase.db') + + # Во-первых, наполним список игроков + self.ui.StatTable.removeRow(0) + rowPosition = 0 + for user in self.database.getBase(DatabaseName='users'): + self.ui.StatTable.insertRow(rowPosition) + self.ui.StatTable.setItem(rowPosition, 0, QTableWidgetItem(user["username"])) + self.ui.StatTable.setItem(rowPosition, 1, QTableWidgetItem(str(user["points"]))) + self.ui.StatTable.setItem(rowPosition, 2, QTableWidgetItem(str(user["minesDefuse"]))) + if user["games"] != 0: + st = str(user["minesDefuse"] // user["games"]) + else: + st = '0' + self.ui.StatTable.setItem(rowPosition, 3, QTableWidgetItem(st)) + self.ui.StatTable.setItem(rowPosition, 4, QTableWidgetItem(getGameName(user["lastGame"]))) + + rowPosition += 1 + self.ui.usernamesList.clear() + self.accs = list() + for user in self.database.getBase(DatabaseName='users'): # Обновляем во вкладке Профили + self.ui.usernamesList.addItem(user["username"]) + self.accs.append(user) + + # Заполним список вариаций сапёра + self.ui.gameList.clear() + for gameDir in getGamesDict(): + item = QtWidgets.QListWidgetItem(getGamesDict()[gameDir]['gamename']) + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap(f"source/{gameDir}/gameico.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + item.setIcon(icon1) + self.ui.gameList.addItem(item) + + self.show() + + self.ui.usernamesList.doubleClicked.connect(self.importLogin) + self.ui.regButton.clicked.connect(self.register) + self.ui.loginButton.clicked.connect(self.logIn) + self.ui.play.clicked.connect(self.play) + self.ui.logOut.clicked.connect(self.logOut) + self.ui.updateGames.clicked.connect(self.upDate_sapers) + self.ui.loadGame.clicked.connect(self.LoadGame) + # Список сохранений... + self.ui.SaveList.clear() + self.savedGames = list() + + multiMethod = corelib.multiMethod() + + for save in self.database.getBase('gamehashes'): + if save['game'] in getGamesDict() and save['userID'] == self.database.getBase('gamedata')[0]['activeprofile']: + multiMethod.append(corelib.Method('getGameSession', gamesession=save['hash'])) + + resp = multiMethod.start()['responses'] + respID = -1 + + for save in self.database.getBase('gamehashes'): + if save['game'] in getGamesDict() and save['userID'] == self.database.getBase('gamedata')[0]['activeprofile']: + respID += 1 + if 'response' in resp[respID]: + if resp[respID]['response']['continue']: + item = QtWidgets.QListWidgetItem( + f"{getGamesDict()[save['game']]['gamename']}, мин: {save['mines']}" + ) + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap(f"source/{save['game']}/gameico.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + item.setIcon(icon1) + self.ui.SaveList.addItem(item) + self.savedGames.append([ + save['hash'], + save['mines'], + save['game'] + ]) + + # Сделаем таймер + + self.timer = QtCore.QTimer(self) + self.timer.timeout.connect(self.timerVoid) + self.timer.start(1) + + def upDate_sapers(self): + self.ui.gameList.clear() + for gameDir in getGamesDict(): + item = QtWidgets.QListWidgetItem(getGamesDict()[gameDir]['gamename']) + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap(f"source/{gameDir}/gameico.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + item.setIcon(icon1) + self.ui.gameList.addItem(item) + + def logOut(self): + self.database.setItem( + 'activeprofile', + '-', + 'ID', + 1, + DatabaseName='gamedata') + self.ui.SaveList.clear() + QMessageBox.information(self, 'Успех!', 'Вы успешно вышли!!!', QMessageBox.Ok) + + def logIn(self): + if self.ui.loginUsernameLine.text() in [user['username'] for user in self.database.getBase('users')]: + if SQLEasy.compareKey(self.database.getBase('users'), key='username')[self.ui.loginUsernameLine.text()]['password'] is None: + self.database.setItem( + 'activeprofile', + SQLEasy.compareKey(self.database.getBase('users'), key='username')[self.ui.loginUsernameLine.text()]['ID'], + 'ID', + 1, + DatabaseName='gamedata') + QMessageBox.information(self, 'Успех!', 'Вы успешно вошли!!!', QMessageBox.Ok) + elif SQLEasy.compareKey(self.database.getBase('users'), key='username')[self.ui.loginUsernameLine.text()]['password'] == self.ui.loginHasloLine.text(): + self.database.setItem( + 'activeprofile', + SQLEasy.compareKey(self.database.getBase('users'), key='username')[self.ui.loginUsernameLine.text()]['ID'], + 'ID', + 1, + DatabaseName='gamedata') + QMessageBox.information(self, 'Успех!', 'Вы успешно вошли!!!', QMessageBox.Ok) + else: + QMessageBox.critical(self, 'Упс...', 'Неверный пароль!', QMessageBox.Ok) + else: + QMessageBox.critical(self, 'Упс...', 'Такого пользователя просто нет :(', QMessageBox.Ok) + + self.ui.SaveList.clear() + self.savedGames = list() + + multiMethod = corelib.multiMethod() + + for save in self.database.getBase('gamehashes'): + if save['game'] in getGamesDict() and save['userID'] == self.database.getBase('gamedata')[0]['activeprofile']: + multiMethod.append(corelib.Method('getGameSession', gamesession=save['hash'])) + + resp = multiMethod.start()['responses'] + respID = -1 + + for save in self.database.getBase('gamehashes'): + if save['game'] in getGamesDict() and save['userID'] == self.database.getBase('gamedata')[0]['activeprofile']: + respID += 1 + if 'response' in resp[respID]: + if resp[respID]['response']['continue']: + item = QtWidgets.QListWidgetItem( + f"{getGamesDict()[save['game']]['gamename']}, мин: {save['mines']}" + ) + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap(f"source/{save['game']}/gameico.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + item.setIcon(icon1) + self.ui.SaveList.addItem(item) + self.savedGames.append([ + save['hash'], + save['mines'], + save['game'] + ]) + + def register(self): + if self.ui.regLoginLine.text() in [user['username'] for user in self.database.getBase('users')]: + QMessageBox.critical(self, 'Упс...', 'Есть такой пользователь, ЕСТЬ!!!', QMessageBox.Ok) + return + if len(self.ui.regHasloLine.text()) > 0: + if len(self.ui.regHasloLine.text()) >= 65: + QMessageBox.warning(self, 'Warning!', 'Too many symbols on password!', QMessageBox.Ok) + return + + if len(self.ui.regLoginLine.text()) >= 65: + QMessageBox.warning(self, 'Warning!', 'Too many symbols on login!', QMessageBox.Ok) + return + + self.database.add({ + "ID": len(self.database.getBase('users')), + "username": self.ui.regLoginLine.text() + }, 'users') + QMessageBox.information(self, 'Успех!', 'Профиль создан!', QMessageBox.Ok) + + self.ui.usernamesList.clear() + self.accs = list() + for user in self.database.getBase(DatabaseName='users'): # Обновляем во вкладке Профили + self.ui.usernamesList.addItem(user["username"]) + self.accs.append(user) + + def importLogin(self): + if self.ui.usernamesList.currentRow() >= 0 and self.ui.usernamesList.currentRow() < len(self.ui.usernamesList): + user = self.accs[self.ui.usernamesList.currentRow()] + self.ui.loginUsernameLine.setText(user['username']) + + def LoadGame(self): + global game + + self.hide() + hashK = self.savedGames[self.ui.SaveList.currentRow()][0] + mines = self.savedGames[self.ui.SaveList.currentRow()][1] + + game( + self.savedGames[self.ui.SaveList.currentRow()][2], + gamerID=self.database.getBase('gamedata')[0]['activeprofile'], + databaseObj=self.database, + gamehash=hashK, + MINES=mines, + muzicOn=self.ui.soundOn.isChecked() + ) + os.abort() + del self + + + def play(self): + global game + + self.hide() + hashK = corelib.newGame(ID=self.database.getBase('gamedata')[0]['activeprofile'], mines=25, fieldsize=10)['response'] + mines = random.randint(25, 55) + + game( + [k for k in getGamesDict()][self.ui.gameList.currentRow()], + gamerID=self.database.getBase('gamedata')[0]['activeprofile'], + databaseObj=self.database, + gamehash=hashK['gamesession'], + MINES=mines, + muzicOn=self.ui.soundOn.isChecked() + ) + os.abort() + del self + + def timerVoid(self): + def getCoorectLogin(string): + login = '' + for symbol in string: + if symbol in '0123456789qwertyuiopasdfghjklzxcvbnm_-.' + 'qwertyuiopasdfghjklzxcvbnm'.upper(): + login += symbol + if len(login) > 64: + login = login[:64] + return login + + self.ui.play.setEnabled( + self.ui.gameList.currentRow() >= 0 and + self.ui.gameList.currentRow() < len(self.ui.gameList) and + not(self.database.getBase('gamedata')[0]['activeprofile'] is None or self.database.getBase('gamedata')[0]['activeprofile'] == '-') + ) + + self.ui.logOut.setEnabled(not(self.database.getBase('gamedata')[0]['activeprofile'] is None or self.database.getBase('gamedata')[0]['activeprofile'] == '-')) + self.ui.loadGame.setEnabled(len(self.ui.SaveList) > 0) + + if not(self.database.getBase('gamedata')[0]['activeprofile'] is None or self.database.getBase('gamedata')[0]['activeprofile'] == '-'): + nickname = SQLEasy.compareKey(self.database.getBase('users'), 'ID') + nickname = nickname[self.database.getBase('gamedata')[0]['activeprofile']]['username'] + self.ui.gameinfo.setText(f"Добро пожаловать, {nickname}!") + else: + self.ui.gameinfo.setText("Войдите или зарегайте профиль, чтобы играть!") + + self.ui.loginButton.setEnabled( + len(self.ui.loginUsernameLine.text()) > 8 and (len(self.ui.loginHasloLine.text()) > 8 or len(self.ui.loginHasloLine.text()) == 0) + ) + self.ui.regButton.setEnabled( + len(self.ui.regLoginLine.text()) > 8 and (len(self.ui.regHasloLine.text()) > 8 or len(self.ui.regHasloLine.text()) == 0) + ) + + if self.ui.loginUsernameLine.text() != getCoorectLogin(self.ui.loginUsernameLine.text()): + self.ui.loginUsernameLine.setText(getCoorectLogin(self.ui.loginUsernameLine.text())) + if self.ui.regLoginLine.text() != getCoorectLogin(self.ui.regLoginLine.text()): + self.ui.regLoginLine.setText(getCoorectLogin(self.ui.regLoginLine.text())) + if self.ui.loginHasloLine.text() != getCoorectLogin(self.ui.loginHasloLine.text()): + self.ui.loginHasloLine.setText(getCoorectLogin(self.ui.loginHasloLine.text())) + if self.ui.regHasloLine.text() != getCoorectLogin(self.ui.regHasloLine.text()): + self.ui.regHasloLine.setText(getCoorectLogin(self.ui.regHasloLine.text())) + + if self.ui.logPasShow.isChecked(): + self.ui.loginHasloLine.setEchoMode(QtWidgets.QLineEdit.Normal) + else: + self.ui.loginHasloLine.setEchoMode(QtWidgets.QLineEdit.Password) + + if self.ui.regPasShow.isChecked(): + self.ui.regHasloLine.setEchoMode(QtWidgets.QLineEdit.Normal) + else: + self.ui.regHasloLine.setEchoMode(QtWidgets.QLineEdit.Password) + + # Запрещаем редактировать стату + self.ui.StatTable.setRowCount(0) + rowPosition = 0 + for user in sorted(self.database.getBase(DatabaseName='users'), key=lambda x: x['points'], reverse=True): + self.ui.StatTable.insertRow(rowPosition) + self.ui.StatTable.setItem(rowPosition, 0, QTableWidgetItem(user["username"])) + self.ui.StatTable.setItem(rowPosition, 1, QTableWidgetItem(str(user["points"]))) + self.ui.StatTable.setItem(rowPosition, 2, QTableWidgetItem(str(user["minesDefuse"]))) + + if user["games"] != 0: + st = str(user["minesDefuse"] // user["games"]) + else: + st = '0' + self.ui.StatTable.setItem(rowPosition, 3, QTableWidgetItem(st)) + self.ui.StatTable.setItem(rowPosition, 4, QTableWidgetItem(getGameName(user["lastGame"]))) + + rowPosition += 1 + + +app = QApplication([]) +application = app_win() +app.exec() +os.abort() \ No newline at end of file diff --git a/gameDataBase.db b/gameDataBase.db new file mode 100644 index 0000000000000000000000000000000000000000..d53e4e8b8940b039014af2459ef1ec3adc828d04 GIT binary patch literal 118784 zcmeHwYm6k7U*3}?AByG@FZ6-iM$BtS9Oz zn%;;HnM+xggv6l|U`KF3QO*zNhv6hZ90YL!U`(iM|JlMNkJ5gkhZ_Mb?^D^J$3K7=RWJxpZfTfSE{4sOE14rzH*do z-LSR2z4ei!qphv|Ter5h?xg?oe+hwI{)2wE74Co}^cv_j&}-oBu7Sfp-1;{cA9-Z^>kq$D&YrKX z{>1a-abJDqON%QnJ^M=a!WW-i6}RDJCa+u6%*K^j|XheD%3i z8S2Qw#`*m5^6HNxBPOeo@j6?aq2-n8`Ng05^wXa}WXfVwrgIOxdj5|6i+A3+{pt&K zR?U+|`AW&Z*I7#A(4+ess>@GYE{-0%{P?4v`S>$O_eT#Led6h7jz003kAM8=lf{!y zJz9MB=!Y(U_Q9ia{>qh~tiJg2OUo(^nnMye$%`6 zFJ8R3{TKJ@{Fq{0t^YY=<)jX>vhkTuJo%~5T-L;rv(W<&+VAqHjm`Oe@W_6%N6ve# zGt7}~xke{fjjC;Bj6@j^9z9=PedP((IdQ#o<;7PdO-SwoM?d`JGaq~UGtV3qPyaCK z<+kfCe(2)%)|D3*)lW6`=GpQ~uaF%2``KC<&&Ku7&frhBzG~X}Pwwnryz8#*>n>ea zxB6G#e`o#o*YCo6Hua~vYVzNpT}#6fesn)-npSV4QdJRN{qQ{v#glItMX=61*t{*g z17Dv>N)^Cq21llVb&Ei)eIR+?br+wwt4qzIk!K^pd!*j8M#bj_e|zh?L9{jaqrtx$ z91s4%;92^k|F73TuYq0zy#{&>^cv_j&}*RAK(B#b1HA@%4ZM>z5S`t=^bq}jcxL<3 znZ3uq^!)Qryj+z}Me%4fisCQ5_=V@oFK_*$hp&F+>QT9%$(ZZ@{QYOQhg)a1hi7)5 zp((#-UV8B>pML(zm#eMghpv`itX@8v( z{p9oIS3Y(JWPOShe|GEa*{!?JZr`&-V$be8Ara45;%+^A{R1ZI*-ut4Ub*_C$JW(o{_UU$22) z1HA@%4fGo5HPCCI*FdjCOxNXfQ><)JB*;bPQ zJ2&jyyRD}Y&fK{3!1eZA!2ZrH^)!Il2ypA*XHA*z9Q>cb{~G);?F9J4!S4@#ckmwv zzdiV^!M_~*#^9d}er@o}gTFWUrNQ4E{Pn@l4}Na&v(aQcI&f}D-^n^ zh1d1Nr8%+iWNhixksRb$lix1&$LXZ47(mFa`#w;32n&)zP;nAuz4=v4oV}9>Z!~67 z5Y4&IVY+c^>)^jWw7vb>bHjW*Sn0F0MKM=fg{akNT|}eS3{Wj~4FV@$DUTyj zMp`yI|J`CG#VL_0#|g8zDWOvoF5#xhCEmLHsiJI@h~>p&*9dmxF-Ra(Zz3WlG9G$K z<63^kwD#AMVOk|=IU6st*)+*UliBY3xV3YExH50}%gOR!*Xy*MQ_3tzxK`hWVhT52 zlyQh4M|9*mES#eK<>ETQ1q5;gM{b;;Z8pZv72y6IvsF<7o0MIWi+J32_M9Xhv-xqP zw-FF8W)svc+sQvCbp%7@@uanqMLZ4G3@D^fXky#pBF^o`<>s43*CeWjaPH!q^B1#2 zDUZ0!an2}#uNgqLB}ztIJ|LEyjO zpGmpxLhW2!?y)&fT$uuWpZCZrXe)r_jNKQ+gr1ZzlmCuba>QAt4wi)q^2Bp8{XhvP zSG4u{3AG-5P_R%$nbUfrtSxL2Nj2=^02^nXGZ;-l&j^ov0@9i_=0a3e98e)Fj?lzf zPm0yjre`sZbW2N~SR`U$1#D6nWo<6#sb#MrC~eyT0ydM3=t7W9tqvicaFqc@gLqq{~X*v|C~Ed|6Fet{rAlYi?cfhe(cKdc7-?A!+qfA#Q_7d|)m?7{yXJa+#4`N@Ur z&)qioD+hn>;2)p=26_$j8t669Yv4Pnftz<8+RpQQGR_w1 zc#%$$cry8z37pKbQ8bCND2k?WG#)=;0%u9JtfDHNR8>69$BV~J;CQ^CnV<29hT}9X zFdbhufoZ%f<8+)=(QF(|=H+81aFNZ%<4HDMrlV{=jprXVf%80@&eP>AN+ zWtJn#NHt34i^;N_Egm(2voe{@r^|{al&f@^O+R7+vnrpBlVmxYO!84wPCs17jH@io ziDXzh1qw0eua50@#^HGvd(rPkWMrm#W$N6GBiAQmnEEf4ZpHEF- znJ$-kl}+=>bT+NV(}@X;7V(_M+u4-VN}G$9nF*ZFlVmZivhjFE`5dKbojj*E6D`x} zVmY0c<#cQUmo#Tjsix6%wk#*3abg0qMV2M`cplG^Q5lt0YyzjV*(lEQYB^14l%LHb z6IjJnJ{pbVY%-Z7`C>XUf#YhHrb(R5M`@lUv+`jRn3frZk0*<=OiA_W2Wpw=EXl^% zXqn{MVw8*?GJ%t5PVX@rM~iH+h?dcVb@FIhMw4<{RsZvyF+FY|0P&GKqQQL~{5Tt?G5 zm1vnQmh*U7E$=gdqj^foU(C`4y^B#5-)jPilT@@wX@N5*b&cL<0yC0REf>imo=xWC zX?jl`GfjzRKAKM#agxp_cbmXvzL=4Q<}>rsM$`B%6PV=jqNEXhN|}txS#nedCRMqh z7nIJI)SSlSOD1qp(%Z>ME6Z6zL*L1JYne0i9!FHtWSK6i@tr2HEax+-%w?9$CfQ_` zzQ+V+(@9CiN#`k5m1UCMQ3q0#e4b=+I?tlfJigroQq!b}vvib>#`94$yUhgVX*S8L zC@$kHpU30nyG>w4rKx7KC`y-koF@526BsXOj$@Q1^YLgB#nb7nb<9~BrzyRhC6#NO zmbaL|X%<&8E%cOmUXGGUa0qP)=rQuUvd^meDCyozaY>IM^7F6Q$|J}t|Hs?%sT8`Lq&Dw)hH(*JT+ zMR|3>1V+`IrnN>{G%NGTVws*dfk_!Bc~s8n&C?s5r-vqx8sRj{=2aXelX#Yn59+{V zIn5?Xl~Jq9^5x>337pO&dOvZQQ+PUw%j@gFNm)*^#k3sHNVT){tO+bD(gRhJl4xe* zGTJwR%V|s%aXcEO@jRK#qU-9Iqj@$OlP;GNst{SZX9Ba)Vwq1Di~WLr%nkzQziU_b>&-w9Uy8?BGFc?^C{B`o|9`XX z1$*4Ff1^GR;qJ@lT_64ae|zh^=mlGrR_$=7jeE{UH|U=>7V%yngRD(cD@9_CVGT_ZB5u^w8LJI@7 z_K=5Q^wq)wPeoielf(%KIAu0Sd(Ou9)eHs|E{} zgD=up_!;(D%MlokBnKNnYk;chR(Neh5JG3_c%Lp?0g9uDu(KWrap@uQEeI6>`V7VjSPECL$J5787rvfO+o4G)Yz!MIDplv}6cGX9^2D z9bi+D6;8$!uIW1LR{P>b%m%V0vawXm@0JgQP`8f#CSz67Qy1W*S@H@jx;UvQ9e>mO zt0JE$xyYx3c>3jeRY8kwaR+H2<|fik8i&{&+f7o=KyA}QUf7~5lI=^n(kIvSx2%uh%d7)Kwh>vL;8UdZk=rB>Ce>^n5bjX{rUgKvd5z1)ku!# zf6tu%uh)-pr$7P3b!21Rh~}nz(Lzj!sw~X`@Fxn)_5f3epxczV8Tv7Q+1|1g-0io5j1$$CFKlf|mo@Ji-n*llY1O#C%y$5%F7fvo?h@EhxA) z5ZguQh|{`Q`iHpRIu5#1h-clcHwz7LKCMo}(Ogp(y7YSTr|L}zlb0iTbV{UX-hAQh z8zc^A4)EF!UvFCaYcCDmQ%t+}cQ_6UqgP#JC|c>-yRy5I0T)`G9IZ_w{xWXl{n< zsX}}?9rfn2G!GL%wYGU{So)_Pfu}1_6xwd2%R&p7?7XNb>??3kDCf{E=n#u;60-~# z{q5#M;>KD9UIA9H7!fPqN`Rx@9*o^|f(jXSV zmyZxve#A8sF#sS=Kwy6lZRTkkyhf38SubDNC&Htt_d>Wph_nVMJ^3-ozi%t9MhU_g z`6;as#Ub|@3bNwbb^wnkcBlO#f5tMOk|-Ye?M`pv>>z^x05VaI`@wDuqO1UvnpGCF zgNf*5*VvP>n}wJ~K{}P+)P*JH`y|CwF-y%yZ^W?!&Hn$N**f^W{eQ9ZGZ_5$pw~dJ zf$yvaj`Q=zS${nlx)+q}-n~JW>}^LS`^ZBsue)Fu*&W@o-4KJrV&w}QSh?oN)Gr1g zwn@3$y!eXz0^rmfnwfla%7yB4qzp`i5CGcji~V;RBlDU;is=>!Lk^O`N=M(W$TA!+ z&}0@C5|3Jyjrv8m9{>&V4p0-|kP0@I^R`Q&bjxKf@3lKis#HAOp>q*qOi=WRGJu_``!5Q&4=|s|H{z4$7uI<&vu}cg?blG&e*}nHe??d zG3pJ9RBp})0a$h1z-OFVj<5&`?EdC@O1b2rpdcO}K!OES5oiF;4qL>o6pj9Q#D>Fr zR{C0TG`e@&BTWpTo>+AN`yNuY`U zH5_c1sP($8J6SE)F$V`dgPsW~5)#BG8{pVe7%RuP=*>IO$UCC~51hPeRd&TAS%GjB zv|_9W(}m~&WMi!9DkA4nu0&b@5^zP-521yI$MNouFCElo;irf0O=dF6XCvVL3JZ5> z62h~7MGz>Rxe&XXdYlTP1<1M{B6Y1m zDHSRDz}g;5Qn9AWW6_w`#mF>pdof5+IZxPfb+&u6&c_+BvBe;APR=FsSty|oB;V%Q z#w3#LlqkrA?444mXiJbHdMm`&M6K!^3(@TVzp!<9+u0x8z3`nK@bqlG26_#AH)-Jb z$s6h^h))mQD^_=J@`e#!U(O?6{*67b)?M)Dx+m<<^Oh53AU&VV_eWZ*M;4IsPv0k% z?u=)03ueGG0P_9Wx1>=3lKQ2HQv0|*KFvjaggeHfARge+B`TiNuXq$EvSfhjEkwQ` z#1$qLl&e8W+W`rsj_3N|ZdMT&fShtslEP~EN^2VpY0Su^)&+_qZ`2TDEC!@FX&!)& zgqt}5cZ{uKlCNno@i<Y^6+I&I6589#!^n#F?WNKA8xzFp=@Bj3U;VptQ)4 z#)il$T^B`HxRg`Vsr-j|+dL4p<5^t${(-S2uRVIny_tD8Vq3D#gz&x$9wqSK+=4=T zIansg<@VQBJS<`};CL((TMi0)r$NCjM*#NxARmpV)l$i>B0;>LflP}%F!ZH!2xl-F z^X`DwXfO>KAI)oT%V8;!>lGmp&y>kvVAI_FaUtY{}Ih=%}@ZSOHG!Vd> z()E~=BLA%n7UexRwb#;zNH#K7$_B{_;1a99m24H-7(73pCk;{3S7h(>k4Qc>Vcr;g zZE>NVI=Fr~bM8>zy@8uA7ZNW{S3^t~ax8WS_-}61W&o|qhR1HTYgr&yZZ~4d)B)LV zo(9xPG8P<|0y9oLLntCSq>XC-)H8TmFlGxOnf(Tq1IZjC;%z0g^pBFsmR=i+b(q5JWZUGEYgYcSCmF?dumhTvX1kQ0SDM3 z-W*4Z?k%y2rPZWZgimg-6n8)&l!50HM7)DGWH-1f!uMTliM&!iqybRT z*u%m$dE(WCM_W;iVg(HAM&)tO-?IvT=_P&-HOBg#QF@$s_Jy+meE}wQ4pdD=bM*;vEoLf6)?OnmGi@@vG5zmrP2hDT+ zZ1ZVO9wQ`Hq%82eG~frIEx)cCE-ZxnhrB)o5e5YHg<-KC4fB8w$;lybhcGe*QL$*? zp;$}SqtbK6Mfhr@lJaOq z2i2|7>D!OrbxUm*sv&*4+xjxIOfEnMK<99RUKYlSE#MY_#0CV*%M*<_Mua5(nE-i2 z2YMUN6QFBFk999;c^^7L0C4xF3|t`JIYP|MP<8@|6#DLUlRE?f2!k~V*m}|AF)AI& zsw9_zY=kSB1_|)k8G+3QWEgDo>|Z|MCqf9B3Vzd(SXs3>S=n1%5R57y$p>O-Xfh4k zlo~?}X8|%c0k9XI9|415#8@T997+qoQ?TVk z2mp2JtugR`BVUs6Je-8^GB`p2AU_&ncwxnr6QjtA`@V8e#_hT5^v zBoVf;aP?h5herTnLd6nS&Mij7rG60H6kQ2=}$$s@XWfU|%FeuoU0`eODPIGqNLZ@H}aoGaNF7!7|_zUNk~x8yWFnL`mf3cNN6EM8|!i*~+G90$vx= z1JoU2>k4c<1P_uBovXP33xWWsn{wZYc^2eAA|H?#_-%stjK3e>;Xs7CFXCx&8r^#x zJ`r`7Gv@OFBu0&rbYbF1Jbr~`Enc)Z+=*V~0ao0NR9*NYG=LO4@!9Fc#q!JQQ>G`tI4-U)A#K}hG@3soidbVJRO zpK;w$+h}OLUB5S;aFQ*|XsP7Gx>bUjW!s>UXDZ-Y7KscxS@c%l|DAsSsb(C)eZ?YVlyB6^@AqCPE#Nh1AwTzY1-mACgK}6T{I@*Th9%vQ97??GrDYI znUkD+_ee|xw^d={(Jn$(0J5B{5YDjE6ZfT&T;xL&fJ*EH3&UhW6yB6tg7G3)J= z*&u4iDmCZ-54H|(KfAwsaJoTJpQ_hDuYtF{2CjYOJ@wp1GAzsaEUvOLOJ>@td2MUSV0uPBX$!<+gp}~Wk>;h%(Y-3 z@_E-c|z6@1OK!B-#-vin8~;B<;~Pnf_A>F3t{lz5L2lflW=W*du(ube=_{dA$2>&m=$r z00p;!u3v6^lEU@{RvEDlnU<)g32F1N`psb*A=SbT`6v@hwFpDvY0%D&43H!WOlr2w zDo(<)(KP;LKH^Srs_m9Em^=`xlt4}GFg0VKZZSerVhkw+j_N0JJaqM(SFC{@Uv28R@Y z6)&B{JFUD~|NqskgFo2+^_^dR+mB)T0`wYqXK3Jf|5DvOKRry6s$4|VSvp_RsAxRi zy^FhNwgfN@0ODzR-LS-oTf2l11GI$_yJe5+kpSEI1n?aJGCKg{MnFr4qvP$_!w_A7 z9?aUADb~rC+=ezEba#VVZe#fE0tDpJ3_t5Ew0fDkWaP7RA3(3R>0aEI5V5DH`1oQY z1OZUT&RvphkyEIX#alxpH-KFm?1~N0ASevf3rB={$k2ABt9Allu{Y*T2;@Ytw}cJ8 zMyz6?D7>2kNHP0UGuZWjSO9dy01J@rp=V~%k=u8PAb`L*MhZec8JYN06sHS6zVp4c zCHUHKQO2_*i>Bprmc(=$lWg;6OTfQi27pD75U#5d!hWwg52SUUPm)P)1+Yi**o5IMu2R1E4r)Whfd54oM1vFpC zT2UNHs|0qE(pM_jiRHa7N`TFT2LPcuOWO%T(*r&aC1xMrAYG)GH-FGLfUQ@cc?dmn z1mH+-t&7d0BYSO6glok2)snLgV~9gsD+(mD1dLXatB1^^f?U11_TlzBYy0q};c`Z9 z!g7?SWwJ~c)j05Nkb;{s`vtHID}dyM$5@5EHwh7Zu!&ftGGd!oXUn%tM;$?utSX3q zlB$aU`}CDySi``~ny|`;LV=jX$WrxOiH5n6cWC%bQ z13~q}jcG(ctj^N0>5Z`?6g!lJ+a(fFN?n11fk+Yrc;umXb~7L|zz6{VVjfgbr>k{B z7MkJ1%mX9P08p&jy>^U4GyeZgbN}D3?fm9DW9ZbEve&@(mIki<#NBlle-CXxPP5T; zKAKM#FopiVpUG2gg%lnaVCtC<}oYn`a+mgcVs(6ZUonH>a`FHI*e07S6<* zP7~5%9TsE?XR5Odox&vr(KtQ#wb@;Do8KEQ^TljVdx~eH(UKZ}yyIOc%5C5M<$Q8Q zZvW?W4zj8iD<>q}Q83OZIP%)sWbtmyK`q)*jeG~QeP3O?io?k%tv$DC zafcplP~rEkkX%S$U_Py8kc9<3&m9t?4K#qjHw)-h6^tevATqAQMS(n<*1VsS`Q2~XBeoC} z0>R~!NvH%NE?Il?rQ$XN7M62s!m$CJ5{>$VJ3R0ZfHELN7M|ej1PX3|r9{3~kcv`F`04V?&S9>d~B4AQP_JQURcEqctb7x`ks3;hbVSgUIN2~$hM6-xjq#`EP zck?OTNemG$2t-%XY61L0HpAkNU>+sRRkH62nS8Lqb78-$hB%6ELx0`D0BB9K{JqyftM>3 z9vPr&rec9^RseeAGGwIF)I5R%A3$J6UotEgt&>3PZlS6!QwSCoQsj-QEqY8wPT3@` zMIwZV0AOclgP2;C9W0hin)_!ptR*#T036v8(h!}LdXwy_IIFVv7E=cb%YL1yEFJ8t zGOUxo5lqyMMKa_6-L3a-9X@#Wmfal!)_>P#4;Xv(&GVNQSzcz-DSf*vFXQPr-NTV! z_%dxL%KkY!H1Is^=V)mfXw`6fzQ(8kV2Lyp?vU}ME5;KXKqN|b8*2af(h$N@=&p{I z5GP;=D8aOTfaN|x(xJKe1p|PaC%S|zC?8q!gyV@vhHI%git*Zm(p5R=^VIf>^QQgd z3A;8Rpr+3bltJq_LRtfj&I>c40DNqRrqrz=lS&j4jxZL407=+B;usDA2L}?0h#*R^ zk|W4It*6zMuIEPL}hWHV0N?IuA+943oV>p4{L@ z&c9ZFTnqL?q)ww3;K2>`e!5a1EsK}q0z9tv=N=@HH_73M_eUU-mgOS^0A~v%gvUST zNP&JyJi?)Q3^afKVyvWwQIhEnJoG^q2V&BoLCAP%jy0`fp^4y|z*H^Uk*H4+G%|4qD~JU^k$5m|km5iTAJDjjJ#KS}uwAWaxdHPWj3A<>J2?qXTt`6SmZ{Gk zVl3%>J2{6tWV}Bw%X~sYGK#4e%K_vOkM)HFRUQ(qiGU#&fW)$zfbcL3h{tgXj&B~; z7U2q=Em|&f+Eb9_)rk82Y;VFA0kY6$z5WK>v+m;Uw<=O-d0Z73;XZ&_0SLs=6}+)~ zyZG63_ur`S@HvwfhW7Pm0)`TIRIiL1eu4r5VRoVRXJu+g4DH9 z{-xq%47mHYQILpSTbHtfh}*w<>=ShsNO;I7xU04h=>v-G9m+c_`PUMmL|e1)Tzfl&d$MSV!P-!Vg6MdVA^@>sS|gt}5Aiz!f*1poc+Qjii};-n zB!)USWPj{hqO>{o0$l0!4*%v2nZ5okVmnMvLmwb+*8e}abvW4nOFJL52LJsZy#{&> zbZX%E=?6`3_&N=nqj@@+XX9v*Ef&!-+QV1(&}IR+(MkZk%#QCnVg6rVvI2F%!EI zs71pLl?8<>p7l_zBA{U8xY?b*&>+9-B}<}?xL6@~6!|7{bc;HPLLmjfT+U4mrK!Y-P6Sgr{`EJu-ft|*w;mm)bcR2gl+&`BE%He`*?X7kUrTl1 zmL?&w*HXO!I93Y{unNz5TQSaCaj{vDNXajF;vBgJAPdtvbSj7oM`l3Q*4B}z^te`& zHe)muLOoO>wRnx0N>X*{OMPBs1G$=GbF37J3;|dw+FWMSO1k2k6SE;!I@Qk;{9Q>m z2uI!lNUlXlOFA!`2DMs!YJpuvqNMq}S<%F#!t-Ys4}xo{W&(^Mv!8hao_ii2h^!}qe&gy^X(5HXUYv3KBf#aix>oNZJa7>@jPLnvJ zbGCVs%*wqxz3!0R3V&TnwytdAn%gU3JYm4U=)(a>IySQ5$Sr3EJ`O-(@036|09~d7 zd31zMB(=Nc5D|cekc}yo5a*ID^+0AQc=w$mp?$Ja7;FrrrEYgsM+>w~H0PI$C|uMO zB@(T5FnJRRG`X%yXIbgMI&@%XEw3%BEPEt)W`M=vea76Z1$)}|7MdY2y!J!uj+!Nw zt*(re0KUqO_PlbJ8FNLpPR4m__TEKa0qOd3yT-2w3THy(3xLK9);1>nz#a6dXDm>c zAQXlxmdsVfeg}H|$Omc@F&U<1Mpj}xS(Ig3rAxJ?7pwpU`^a2d}!6Et5Q3jFQnF zZt)c+nRpDL7=cKE`hl@GLJUwEN0P#qLg$KDvkFQI^s_veEqEm6kiaCUT)?SdZ(z;Y zHE?KP6@+In%_2X{P20lNci?ER9=kcn4%8F|iiQjmyo<4FI@DCbZ()4hLG&gY0=X>6 zh@&{90WfxmnRH-?CPO2Ed@>VlSMD&;PCCRU6NQ~-cDNM>l|$u2M_7q1m9C7*HdgMJen$-I`a<>^ zc$;hB_)~E`=$H?)1$~_-r}M_MWJLD@ReQIbWL_fZnKxFAyCn34ZbuQ?d%!)Cxul1? zLG24PqV+{UO$cvSayZak`9L!$GJ#@CCCp2t?XbBms5Q8pbzB(aC2X?2LUK5Q#TVGA z$`=$-Zwv2&(MAPQw3;jCYwgJ)W(VG%Mu3*d&F4YRW<^}Xr3B;50zUpNWD;!!>*iEE zu|PWo-GReKx>QV(vsK^yenYiHvC$sHc5JGV|G{y3ig~TI+)j~+&hZ4UJslaF^4hb* z>1;NlWxr}UO_o)a%{$Nf$xNrrOEfi*4GS+m_|`-ZKIGzpRt`oK0I(u$4A_wNZ&(m5 zw`I3~NK;)boZDaw{er|+w4$`7=#svI*gUf2!!~&GLzZD9MlD~gfFQ94jW||@wgsz* z*2ssrl@{R5APBU3yF}|=FS6=4Ierur!NxaG2unGex9)%>n?&V@D4Z63eAlS<5_X1F zT;;T1Kh7qTNs=$7>Q-RC8}}w4{TVmdLjbr52I$hVeAIqP!fo1@PlkmXDL3r(1k3~i z020^sXFAAZU@8+y6+xxpx{DwHnGcX4mRESd#8C`1(8eZ5K~N8_nBPRSMYfhh*nk4Z zN4)%Zkw^vuC6b46EHfq@SR!k3T^?LR9M+zcr;|*4)Ry#~IUG;n+-H68FbFAwMRG3~{)%Eseayd00xy+P+N-EYKx2pn$#@VHN4wRk9q z>&|#iJHUwbLjuYw7W`3Z0s6Zpwdn~+M+FVhTP*NZS_Lc?g=~7sRg$hwo{Mz}_kAzX%RNL3}`Lt>n?1(wV0-#!}k!Ku+SS(>6xUE&l&GOD}li z5CDXk{X&gq0Ze-c$miUXQR+OhP0vu3fgl*5gF!L{3aY_w;}i}RpWBiOqLZGg6#9W$ zkzuzmcq0Xh!sB(bWEODB6ixR+nylzZR#4w$mMlP_9>$SJ8H65qkopTBqSk z9A}TAZx3K@0N6r62!Q!KNTWsoTfW4UP4`rNnlvB)pmlM6s#5aZTL>~hR}RRg_K&WI zGE&nZ74~8x1VB1m1R0<>tneoL5%1-rhRynpcy^MyAQZ*Ma?=eXhsuf`M+uKAF^`M9 z5HYt3MW-G!gC(+ws>&94+LW-_E7O*?t30O)-+&#(?aZDk^9kZ?Kz#yfCsocHU|*|{ zRS}5Su|CcC|JAL-TlZhzdG)(#@YLs}*T6eK1ILHcdU){J;W%H6Cv>>GOcslrzAmtL zJ9q!EIKz-ASu?LM| z)c`D15=f*C0q~k>=+O4~*d&1EK_md6>221kL5hVue{VBWV4fl;;y6ql2?iLc<` z;($ZszyQe{oFEk~mQ|PTvM>+QDOZV32=Rb~rrj7z1a*g=3w5Pr3Q-_kEy>cTS<1Z> zHV*8eb!nQ~<&`e>MuOC&W!tWW13Sd26|2aCos#P^#$!LXhz>x4{SC_MsVZLV;SM?) zgyY^l2dhHJZR-?AhF?BQ+g?|(Bf|h)yodlmW)K=!gRSz)N7&4bUZ^~F*;Tk2dc_)_ zgx#@1*PhF2r(`gU7V(_EXOc~;Wi_J72|1R4D&Fov;H?O-K-`&dcMtp1j>y~R>-G!4 zDugVLEh=brte(klpIDb|v$NXba3p2bR?Bykqo?%SX(85Ia9&qe3_tI3Z7o?7&YD7P zN3=16*pgP5nN4Ju0LXcd#)5DpMv76FfGkH7)7A>dCW{ezY}yVJSSTpkv`>z$5VD9_ zCbA72&yotOpq?rwJL?u(%i+d2iS+OtexY*UJE+v=RZc^>moO@a)6$#q|1WGE{I~tz z-uZ=hz<8@KVy}U>jRub6M@&Eb`n|NzznVn!#h`3jPScXs1|ISz2MV^H>@vX5C0CtI zga_CBl#Huy)>`8>Y+oqD-<;k}ej@#i-hQf2Yw9 z6T#Y-5N>pV*3F0Ejn1^9afKIG0#PcTZO>BAa1R&1}YP-nZ`~biDT~rfP4VN1n@T?$s_K<=$0TJYuWV! zas{Z3Lf)<SfVb19~R&;()CIGnxFlYLh zLoj0G zYM=iyeaA1IjPp32WeYlgw4Cp~=LALqWyH`H0FNx7N^OO=Id^?-KMq0)>>&qmwrAkv zB_H^dw0!N3QxM1o03C_2hO_d( z&z~0}k-O%1nN+d8q2d3$ms%QyLms|OhI9oDo}{G zOq=7*>U~SLK^OBk;XngGQ=lFn9s<-8A&NsDFo%R-!xbfR+lgX}{eC;xuFs>WiuUejBhc+2AZGyB5`+){xC{ma3(V6ovU$KC3bAPoEc~-e z;>a)PE7|L+6EX>a+e{GxP|&29%0J{&bLP6FkZg3JIRm#}ppKju`n{qWz7P%a0YHMW zmjr=6pq9_|+D3{6si=#@5`-Y(p=R$FrbB*f6zJ*ZOv34{Tc|vC5T2YyWX9tC%IjRu zLo=3a%Yj5Flqo?Wo;czc6zc;msyIReNdWXrrc^zIXg+9{FNnsm4$b)gBU=YA>@Rjc z@{S%-^@Z;>aAFM{-}Puc4&ND$%TYX;RHJ0Rm@LcLV(*^r-c{~&`1BG z)EkI{7~_3f&TF^PJALf&bswoc@TZ2eGMUY%%WAQR=?ekbbnn4V8-X&O2+$N%yS;!w zu;4tZpuvRWGZ((b`5Z^V0GTN|Ts?{49k|CM9~68g8XgtS_!5T92ly&G9!2b^K^y>? z5MJ~(NU2C{CFHCCK84KKfy{stG9WEkA9&{qxwDU_pg=(tm;S^_YY*k_?=B9c-vp2m zgW?s!KK3pWO6OKo&`}+Rju`2O7^~5_3tXa|8_LjIK}sJGkvkOfBZ(oPs}vg!#}Itu zh7TJ<@U3!~Rrze3B+J=kl8+*~3S``A2&^XETHdyiBM<1z0r1EGU0NQ2TM?W-jn6q1 z+_e12*5F*r_rJ3_g^4bBwgmF1a6BGTC@^P6rwSAcR1HuF3&IqOH<7QN@szs>QXDjv zmW%oekZeZw8nAi*w}5>;%;3{p0goH8pAHFqU@W6m3iYW# z(F!A^53$hr9}WyV(X_W8t}boEQtDEoSaH;|uV1{-EWye05C_Nrj2kNsiG`J`%x=y) zA{s+;F)`W~su*tzNIz`dX_DdkvL==)j%E=)K{&R7sqr8UPaT+v@V0=leSl*%krIlk z7GhM=Rl%q{PQQCxe6*f7tcHthHXcv1=`y7~Jkxl-H|n+wk_#o=X|gL?GwE7s7Q%c` zc>XdL;{C{0h>~xn2%Nm}oInA<1Aoi_6ufaUuo6q#*p+;^Rwn{xVxe(W^>iolspOl* zK4~o$_7A|5ZBRp53fh5TCzrNU6eXpNv{Xe?aN`M(uzI57R0o`X74cgZY?=YHBoeCR@0-VIn#F9 zkK$9)r47QW;y_?Hkmg%ipqYDRj8p*hR5ILPDyC~xMn6~^mAi)XJe$tb<&5^B&Ev94 z_ulOp75D+JDS?yW1FNZr+Ke9orUU?SeEFfnfL5EQVZLiqXl2|D@}?Pt(wotsOn4R4 zm4{V*YO_3h&K#K2pjNqZxj@xQWCuVA28BHIxbO&|_Bj*<{mor$VB$@Zd7@Ds!~v*h z!4wA`03fjjn0kvxFp<=fG=@E;a2JONsWq_IDaTZpT z5RWz`=pcg}t-H3X3KVi>88n5)R5r(CnDPJa){R?-cb|P^_k9G8Z}?a}WVv!F&-2MR zTcqPfI-!e;CwupLHUxS00h<>*k=D))mR@#SKJuEB+K~hR+l2t+(dt&$l-}NrD{1|G zZG4BA#Nll!2=ReAgdEC$k)xJ+u25jX6oSc_2rUL~Mj@mD3hISe=Uphsve_VIt?cG) z80HQZu~|U;wR5sQysn9?D~)wvSjbL%&AN@@JzyrcF$XQg^LXULM))l3g-d9irLgFmhUfH6gcap!DjNQF}nSF?K8A>8S58X za%**L$AZUHei5kmN7ZH&DBs2mNJ|bsSKt6eh<3fE@Zt1&G|_5-hjCyA*o?$P*RNwG zi6&-s1!+)b$&I-;d}AU>21&vr57kXV+h=|Rq-?*FZLpsg|>rd7rp%xCXJgcu7I*5 z;wmNm%KUcJNM#u;Q_Miix^erF7RvdB3E%)Uds()81LFN!jFQ8Y_#mVdR>F6@;oI*Z z&|ZAY^3>)nQPt9r=s*}N;zSLR6RncRo5JX-E}k?!5r-6o*+T8Et!IkkEpF(F&}rsB zcG)!Z*SeF2D)1F*2aPXr(}Ub zp+z)pB^aWlLl!Iw4*XH4&$~|SiXD^*x}b2r2B6ijYMJPqyQOj-DTWeGbqM|XhNzpO zOZiN_DVLc~uQkC5gOqQm6s_d9j({39kJQ@NwXP6OmO{#+KG9N)okSHz>4rn&DLbE* a2Tn2QQVC#>-nDlN#5jSa&Jg5p?MF#hq-g_+%q%hdHXqY6Gi3p zZ_JoT|EEPwtCoxQjiRWjptpF_zt77^pZyLRH(6e&Bvcdj6zT|#gcd?;;RvCP&{8;1 z*k9O7s3Gt@zW=)zEZyg>)ZnawP+5S>y@fvr@P4$=ML1bFT{usVmgoA#(rbR4;9)A==_DkHNuFak3RbKGtM|8yLa#24xD)6iR-FVsgj}3 zkc$CAFQJP-d~lFZS0H{Mj!4-84Z;r6LY>D>W3RE>*l+AO_Pn=%&fie0R;|0bckjNg zPoF;Snrp6cGiJ1Gpk|4hSPLhx_~V>S2#%^E;vMJAP`4jTPh1FTcAPM zjt%bN0K1HR#!jaR*lp}LcKib2Dq(ojrcIyj)vH(bIp>_?CQX{;Xy1STy<4$jg}d** z`yB6siITC~VL{`lkWvdb=W9Czxe zr*5oQuig}Wf2eSoK-@t5KpfFbI6xr2z!s$Zfd*k4HMmFTvA>Kx99Uj=tX`iaV7IRj z(i=5uG);a!>%8;MbAtvAaxc8_qWk*mZ;X1s{pUab=|27RQ#XJ9eD}l?Pk4X8@!+VV zj(S!7j1-6)E)<9(h$o0E8VST1#2eTkF%GUodUjRopB9R=((_8PlA zszZkk%g;amdKv)%IL?|Pnp`srVV_+PVTjr;iHkKKFkz2{zi^;P%K zLl3!o@4eT@i1eelAe;C>{SOz2Cx|PEFNiZ*3TZ+rPt+ika)W#9s>OKmyzVr8)>pV% z7>T{U@WKlnxbC{^-0indaINO#zU$t4 z>n->C>#w`%)2I8maP;WWK5n4TlTSW*b+u~M#_L>`C!VR-#2dsN#2>^VmM3!Esn}41 zuoW%TdCTkSsh@*|BL(d5S;8g4U~zKm>8GE*u3x`??xKq>ayQ;MTKapRn>%-&Td-iE zTe4)Sd;k6S#WVc>l&Amj|M$QD-F@-J7d{VIzI?fR^UXJP{1W%tYp=OEbLRM5;r82a zcUN9{rKA6|&ptcrzylAQrt=OHs2>t<^bm+YS_s4=78??$5U+4QsaCuZV5}H3biSsr zw~!`aXWI$b-SdU3gyDxAa>&z))v}50ue$0gcgrmq?$Jjd_3_+`FTUvQ`RlJQ@wWYo zFTQk}HWlXo^UptbAAb0ud*_{Z+_GiMy#6mCEMRX32-jnW zmDjC+3u3kO^xVxG7Kn|NGznx{Vt* zI{3#wy#4mu@)b+H?voQPT)5C^V zCkmD)5~mQa)DTj6q6Q&JGw!jMj1~5i929L4!_W%uVgWPqFw{aw8HHN}6+JJNWIR@9F+TuFm5MAS7fw+YDty;BuTGu;VAU+{ZAznF7Xep!##4~0Key_2i={)w3 zu^_K&C{R~BUI_EL5!hG7-r1L2a)}!}c!(Q6{#Mm#9`)Rl+s~f;lm~K&7{8WFp{SrcImXZ2|H~ z{g`W1p12}Go=E(1gz$UL6X8A7d15^5;z0s-va5jIyjZZjZZ&lrbpFO0Z`4@dt9iR8 z+{~G?@_-B&kOgD_e}D~m{`vXhbAj@@x4fPwqTVB^dubEpVuRB>dPqOHW}g1@;>RC$6 zA8bGN9qx^D^n6Re*N}G-7mOM;%G(02U3uaX-A_sK#9d7WOy@1e+e70K&+E?6d*pT3 z$!=!!=+R?2`c9rTeE4uTapD~s+s9;Ur~A6x^yxFSMGkUg!Q_A~vM_7bEbkA{OY#8t zqz1TZ)h900|4O`o<-$s6)Bxd=*zRwC`&&qPq1PKWZ1A=p-G_TPN3Tl?@=t6PdE$r>BfK0d zF38r}0`kNWLYOBK?-2Ji6o`W?Hr(0sM7TGdx4f>A-lIO&K{#FLC-}Thd@muF*BH6| z_ut{SnhIfFH&FXS_T6{iyOlR>q;`Alwb#0|v^1xAd)KjJN7Y>)@gUYW6i*gr__%;P z@a?zX3D_9?7d`LJI=P)pa&j!z}o`WnsJ|s53^ghZcUz;E)ee!_YnWI z6AlrGi^vme3dBo0OP&b#7UNl7m!|hx3D~()g$o4i-*DA(UR8{gjh`GkbeL+Zx4DB3 zI>-b5v7y)ilLNMKIY?v|um{-hb?ep@pYQ}n;t%L2r>11JrLEhtzKA(x@p@&%dE$^EL%bXe95^sDH#Ququ^|p39wII}R-pb(9h`c24S_gnN0Wh2 z=doAVEz9fvsMpvv?AsMWdh_PZrzhAfnfE*N-1#*3Ad}YyCBbHNRv|3}yw!|U^))=SmXxp0ioUFaiRCJb%Zu;CQd zayG&{^JX{Se2dTN@4WLauLmXr$U*bwExZkYf3per1&}WXId%b?K#gzBnzdy~d!fFW zCyeu%Y@b?0GBA&XZ^I5?1CRmMsJCg;CRv_H9Mw<=^TZ0;-CZa&j+C#v}n;nGSHm5S)NUZ%K@@LEJ)3k`dwMI zUw{2IY1dzU^_BMr$+*SW7X`RC+vV19_e5IP1ADbe=i-+ittvugQadlYzJ# zv~1bZAp`J_Jpkq#Ob(dmBQ`JVwt4erNBjp8H9n_a80Xn|EozJXH;@;M);dGRiM)Wd zhUAHQoqgnyM?S6bjOU5ORbigEEn>rvd+hxma<758%~J&Ox~qiYs^L7Z_%56Hj`~+d z#(1yy@NY73yVhnVl7p5lTZ#Xc;=gMl-+<3&jn2RSU2qMzal`v?QW}l5LBIU+i`I+( z%i9>96-t&g#wP|P4(TM|lP(p8iL+^n@3P@NN9QvNa31n+uM^3E=KUNpK>go*1F?Og22bC7 z^NnA_&G-`wps(2Lt(5`v9`5M_c|xw>oOQySi#Y^*Pcj?CJVTsc+ouMU2+R|N++zdC z3y2Nz4L9F>v)3oaRyo2FjrU05YJt4EyMPQd60pnYC^2DaHu}ToBVz{(*!(jEx43ZUQ*v94r2`$>-bFu;0^xZ5k8C4 zkR@aW`vJEh_w>y&f8vUVg<;~0EE$n|57F@I4Q%o$IdIMM4ode0cbGxLYk z36KHi5ReD-iqAEdv~Ius_PbMKj|`kEbQRD^;xX#3rELSS{kse3K0c?1&|kIMyERAq zE!U0wvi7>ToF_XD{;>h%#K{`=nDK{)L^5E$03PAf! zv0j@sTgbo~J=23dMidodHOJaZ#F;Vp5F~H?(LZ&{nN4DRkp}H{CSW z*Irp)MSM%HmQ1gUdY}2Uu&GY0wI9d2CQX|7wE)z^68Tu3)eL2THT*o^gN;T$LOFm_ zlLa^q!TZ4U-FT-D`l3(#406VGn9Vot?RlYvx$8!$+bA~kzaxQNi8uO0|4paaqU**z zHFAJn8`rjla|1D<#f9*p=O6D8H%AE6Wr)>?*-E3qKlTK>a;ToMy%OCgW_eA|Rlz@N zaqhb7-V*W8@heuWj1P~mgX@_pr#VsAxN&1Y7kK>f$NTko;lYff$pE&PHJs=tT!nH# zY=CcuSL``JzwI^e^Eu#H<2}@Q`i6VtEwuR|_tXK=M^*LJ(_zPqbly}FkJYCG{p z>_L8DdX26d@6>*n8zmkzuGt#j=sYq*t$}kAC&5SGzI|tlAM!wIGo{Htc7%Cda*Nhl zmyu4qM-4vYpBg^?peS9p&se|p-FJoSD2zEZ6XILufmsW}TmU(N>Y#o+!m)%4^pIzJ z(M$6O*aCbtazLNR3U(ZB;n=`F;4`!HrtioSI!~O3d~pusjrt=wzS(^A9ck*#wgZWCE}ckQI*MSa|2V#=Ggf z*?YL>zVY|Ss-8jhHi|WCdPd3VHwC>7IPJ94ysx)fFlz@A`2)^L{txGXxCb9X4q%*z z+c^KTHMa~OiW@!>Kg`3V31!B==5%I}gJOfW#J|Y_+ewq|mp*;!b1vhKEn{uG+m?8O zxfAXU-O%$ttdDT*+qd_1p_5KJ$M!>(4Z{hpUe`0Rd*c#{YZ72iC0&`-l2|VG16IeIrl!N+M z8T(D_hF)R|LOH-@!y~@I>;n9T;C;X`9BXzSSqgbKooAkh-wQ-0+qP}%w2sryS5mX) z*)L){uDvL*ewaMd8omda!p~y^O3FWcY)k%$SD1qh`6r$;zHQ6=9>2Xoj78oB|KxFu zah&f&`{)ZX4t9V%0XvCZRqcuSanA$(fX~rubkyVk?vMq1Ho!k{-Z*&Qjxo-WBl6}2Ci0l3CL zYMqSFv9Jp~e~rzBBRJ!_;179#OJu?10htJ4pK+YY58pT5(M#5OQIkUU@IA~;;fq+K zfK2i^*NLw;zs2|vqbFM@&imX8ev~ocnL}bAYR1S3V@&*KcHg#f`w#ydtJ>i<;a{=t z3hEVc{>lH*d*=0#3Ao3uVfV?m7~7(FF6y(m3^1CUg z!?|&7dTyLEo_rr!LMO?u6!ZGu3D)n5G5_XcKA!F4IMxHgWpSPfv17P?;&#Rn;J=CK z@rU%q*diNT6Y^o)vrWW5eUvf(D0u!+UtcuU8i6Yxsz%lXk!>@)A%d<62qcwm?54;?}d zwgmXcE+7NQ8n%R-oLCI|f}Nqx1}DUeaL+TM=xlKSr_}M_p1EA(Ka>IFfa}NZBPUjO zBrZkP`F>pgkq`PRL;efY|K$Il;QFzXOP4P5erxgK*Zerg=Rt|}sj*Xwg|{v7o9J`6 zK;Ic-#vAZFXIuub)%XO)kn3Z9gni<1z7xg)=JSnj(|cr!@kQ^cK@o$IlVR(*ev4i0 z`Cz-AqPAQ&wjMu%Od9{mWB@yW3}6?~9bh$Q?h*gS9D>z9U(Rd30J%lp0Aq%Z zW1l!b_lZA6RvCBWf8@xKE5-le0(m<1y3(ru!9VkV%qurjoHZ30Mz2<_`o!yZGVbkt z=3ALx!wz7}h;QS3+djSmf66!)2j~H@GcsWQfY;a!Vk-27*T@s+W!%US419-NoiT?W z>@oU>KY>$f71%LsL~*W%W6*hI4_jh>JLKQ&fY}1`1-y?OhUdYyaE}(JVt0{a;u@=8 z(vN)83h@)p|CZMLpUwZXuB5Si;=^1g?q?@&48T z8GzeR4)B?9k4)iz8C!IR_Zdfc;@rqGz7>w(f-SO2&g83FWJP&Tt8!G zcCk1zV6gzX;2O=xaNWo>+`tL<27jdmlLNN+1h|J6#*uw^w;CNjg8B`91ASv%kTvXX zNv;Vwq3^{x2iJzoaxC(NO=A4uW8AoLe-%e*0_(I&bN!dP z^J@w40k!34?&cnlYsL^;RNNNE0@yC>06vWSfTyyA*=2kx`2yT?4;CYGFU$kj8YAwB z+C@pPsVM$A2V)121+Jx}{c}9u!=KR~vP*x8d*2p6tkGev9^a4NmR2(xP*cCbcLaKZ zz2y9W@nlU!aT>9Jt+B!%U}up9VjOH6{447g|6Z6;UE1c&VYaO2j~&}ww>6`cDi}lD^`{(l2s~&JkBI{&(Mfcc$h(&JizV$-Nf<{bzQ7c(A&1rCaa0=bkLC zgIp`L1;kBTYRC!xfcgM&A-MrEfF4q>BlpFoYz?sSj1}{4$N)YQ8Gw6q%KF465JMUN z*fs8lYq3TgV8?H1i=8ft|1Et#(dYCT>O4BfHA)9xkUwT!9_#bbP59Xsjq_kP@iE36@x@$V(tN4Apj&ow0aPEqe6v!Tv2&br1d%^8I{k8j>q+?#yG zZ9zKMg$&@ALR)}uFRGCnQXim3M4fPr@8Z7-cS-s4;30Ndkj zfyQeodW0_xZGnwRGC9D{vX(0@1B|)VKEt|g_!=F7GirC#LeOEZfpNp`Gq&OJMZHEB z`5yT?^F6HXWKBNTkDPKnMSUMRw0n)`|LA{F-?h)kQB3EN<6K{#C7ImbIv?lW_6yj8 zKmYm9>C(Y0WB^-W{(voZG+86=LI%hs@wv>kVFyeGhzmnt%jut5JvIAq?uYk@#lquD z@){25lXbW@4~R@)%RsWexTd1`Pxd|DNA^tT@nMV~P>i*s>AdkC-{#o@&B?cr9xmnj z%@&&+gnl)Yg)lFqHefP9UB+a<=I$6{o1;TV(N)$_Fo#0ShtIHMw|0x&z@MM zk(V=$#Dv5I+Y9JC&jawhGS33>oFeOklkso7bHCg-kiFkkb>4W7Z}V({bh(A@e<|Y; z$^iW09T`a*u*pSTo2Dt@;oKa(y_L|);*9fP#>aBOnn|XKn9pI+TP&W zu>shFqWI@H#r6yI{Lq0KY}V;)%ewux;$Joj(U|LbpP`C2UVLslP3 zBm=fqc6)Bg=kQO-_?NxkBwH~|gVR`GoeuLhjKS}@6|x1?3wXYcXDg|}p)<_SpzEv| z0MrPn%bOh_X5jPf4P+o0|I+*L84dofLuswi-4*1gfGv`(lM3;^Oj){BHP zK#qVfpjNy+!JG|pT1@^+^BMhJfq(9yfGtpdmp*ms)GXp`i=B}H>IKwKST|yJz+`}& zVOxXv)7F%dzohv8&EiQj%i!L=ACV=K}!FBGxyd(0b8Iv zFEpaCGJgx(o61Jskt3e551kI;EI=UTM?gMOoXn4S+Fp5nh$cn>=%U<*_?OxL() zq5F)##fR{KJ!bt7c>&wf1UZK3Jp6NhpnA?r`u#LYdVjdan2{7d@@Tp9W^|jjpO$b4$!;9GhxFS9z4nz*N7UUAFAw#x^KjEEo$@jdV8g)Hg9Q9j%AA!Gp)?%lo$Zc&|i2o`_0-5a)e~ybE`98OXx@2^;#F+L-&+uu8wo3_}|8aCpcXJ zTOhvErMFqc>+l%21>}azBO2H@oyTXuJKvQoB+~it^eO(s(b)d43)lkrfu+Q6xm*^o z1jr%Ru+&~Nw+Z2@rtzJWX* z-Z=)H*BUco`_RuPyBYjH@gMPjk@=t2Iee^RjusjU%>VHmWvZ3k2;|uUt=(;bEuc=A z%dzL38X~rV_Z2S`SLeekX8lIq-$&5e(4q8Ci$DL9%Rhg+Gd2Goj^Nh4hFjBfbPyiM z#Q=I`K!=Ij(9zw+_i*WXCeMt8P>k?IuFmrt2lPeXTWTD`dC=uF;Yj^PAHR9VZxjLe0IabwP^07TBkrvl?cvhJC*|MY zP+b3_o*O$~?=v4yKgbGwa}Hz)S>U`NejOagdHLHo&4mtH|93O%_?eGl-P*Wu96lXm&dc{p^LHB6-A>&?-qCe*107_%(NXw@ zqt*f(wi7xECkW`&DZ=T(8A30iw{W)5Cl|avOW-q(;aIphNjOpHD$w8Y0(^1~WQ6k} zU+~QNK}&)6k+aT1PobYMX#YPBn_Iijw=Qb)mW%dz)J0XNx^fj?aMhbEca2Ye=N~ni z54cXp9VsAN$Tf2Qdv9=GK|tSY2(<+CElp@9pf7OJML^Hs4_+@2`U{r`mkL)1R|(e$ z*9wCK#+xnRy{iR|;n>TBfx-ZRe)fAZWP7{Hwj~fn}u5fjLUtG&p2j`z;{Lo!v*@Gzrg~1BPYlR++Hl0jP%caZP&r^ zBcp2jPmFqP$}T4V+*g{ghfq=YJ+^YX>GDD)p@u-*g->iMU>~uo=-+t)V~y^@4_w3F zEfs2Yc(mev7d~6Le#RS>>)gDoa{bvGD(yRKL&ZI(Zm3Xw=Kocw`l>5e{v{Wcn=j1H zj>_M&Au2y{LsW5saGPEaTozRtuq>*0+FzmyN6(5Xw0KngW(dd%axz9hJ~*ez2y((} z-X9~}5;Yz5m#F9W+0mIlyA=N#ku5jlzOoM8P1sjxDBx$07fu!69o@TLVEo5cKj`vj z_GtU~U-xXca6|1*U%FcDR=YipT;+B^)b#(Q;R{{+q7yK1*E1Jr=nF{Fk5S zq6*KtsNz#Dsyxd@Rc3hK994V7Mb)RcsK#U$?RKAw_L$_NT9dM)+8G<7y+?f%?LBB& zRQvqDM0<97GTN*Atf+dk2cufYO^x>LIXh}`=bKTdPqL!Ze{fN+UtHAnn-u>dY75P{ zucG7dfB5+W1#}+YdX|8_h4)ca8+3oT_KEXX?$>)mcAZ`u-M*)N<@V{m-jyl;dq3o& zI@4Tqz-$*an(Ly2|Kg%U{>MeF-*C}U@4D!ZAGxT*=i+;Vi+cRs8UN+ltV;1O(u!zt z?(6DU>^*TbF%~+1rI22;dEe=E&ii2fA1?dJ)$gC>>YewU+tK;2*m`A(f037@8TV%M zjr(JDJn>yWVNiwL>W{D6fBEW$H*9tduKC&RO#D|l?1L2lNyyjR7VFj2YhwI^1-L&= zAf_I?=P|=(rw!ed-FSp^J0t&l9+To^+` z;$OqEOEq-As!&Tv6OItN3wWvs&(AxF-f^k@xRNEuN#4X@_qQBj~Rcs`umLfmwuN~ zzu&hR_0In~qfYPj8T+30Wyan;R%h(h<&#N!b@*&atz$k~Q6}?$TRH!e%Kr-sfZjm{ z$N?)g8ve_?hUwYU#PPWL*L>Bj{*~*G()-K<5+hI}z*ZRePBL%|$Ac=>n#?=8YLmr7 ztEMfPUn=vzDgHIArP`1Ivjx}#VDGX0EzSBxCaOf$hu5vN*9*5+sJS3(tLOhy{Qnj{ zw&!<|1#E`w$rbg=Ri1l)-uj=CtpC_b{&#Den*Z6JCcfBxX_K(9hMs1jAUzgtvs>WrxJ)J)dqCtUwm z_WZNfEw%o8+X@MCV6x!zhN#j#y`w6VW<*sV%q+0}vn=_)Xw6Dp2hTGcAW#=0ZcVjq zI|89BF#eUJYB#lws^2~&+Wn5_quP>(tyuqA>97yCxc;l|fVCg$d@Tj$!kHH%UQM;_ zI|61G;37vJYL7iQs`o(eC~a0o)MW0GsQH4-(pdjn|Kj!U=^O_M`v|OuA{S1z9cu(a z`(QGW<1-F@`GlzLTbD%bKFo+Z{_l+FgbhogQ~tT$umA7yz1RQ!`h4k5>^H{^yK$-; zF=~bzblp>~-?i_%Ru|~`uYupbqg!PzbWefWSBifP!cNkX$pv~0pLGuF|C0Y(ZuWP` z$LaZh{&r}eK7IJTPfdkK68TSK3zw)2jF`3`is<}s%a%j9?G;{5mQ+0*Iw zq1|n>KANxN;2wLD(tQoWF3^wx{6wX_y3TCd;gm~W=Wm5hm@t9g{P2IH_UdbgyYctV zn^B|FOyxk=bRwu&R_A`{IB~HEDrbE>qjcW)*!aOhYL@~35(vZ68pSLfy?6p!^=(W7aos- z;~O8(!sB0vzcC@7;&*MhKiWyP^BWQ!5`sX67vNQHO#bl=^toG{zdxG&phI?DT)y)= z%+=Vu?>F$r=XcmXXMe37kh7cDq3NGpah^rVJw9)LOwJp5`)gzSxmTR`d2WY!`?*(~ zx1Xzm`TL7~hxz+C9p>-n3_<=r9aao2p!VyB`|7Y=xW6`vHi!G!QMt`|_Qwx-6}^=^ zK1NyIe(wF{?W=cM-hR$*-o6oJpBsU;Zv@-^+SvCO$M(m>_H%dh&KKLa)7$6qeWTkx zkMG;%+UKRcA3xu(<#Kh##zQptTMqUYbC1EkAHrbY_ZjSaEe`kB7W}@aIQ-n{fCBrv Zgo678 + + Form + + + + 0 + 0 + 460 + 341 + + + + Сапёр игровой лаунчер + + + + source/gameico.icosource/gameico.ico + + + + + 10 + 10 + 441 + 281 + + + + 0 + + + + Игра + + + + + 20 + 30 + 391 + 171 + + + + + Системный администратор + + + + source/sys admin/gameico.pngsource/sys admin/gameico.png + + + + + + + 180 + 220 + 75 + 23 + + + + PointingHandCursor + + + Играть + + + + + + 310 + 230 + 121 + 21 + + + + PointingHandCursor + + + Включить музыку + + + true + + + + + + 20 + 210 + 75 + 23 + + + + PointingHandCursor + + + Обновить + + + + + + Статистика + + + + + 10 + 0 + 421 + 231 + + + + + 1 + + + + + Никнейм + + + + + Очки + + + + + Обезврежено мин + + + + + Среднее количество обезвреженных мин + + + + + Любимая игра + + + + + Игрок + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + + Сохранения + + + + + 10 + 10 + 411 + 211 + + + + + User, 22.09.2003 14:50, Системный администратор (не закончено) + + + + source/sys admin/gameico.pngsource/sys admin/gameico.png + + + + + + + 10 + 230 + 75 + 23 + + + + PointingHandCursor + + + Загрузить + + + + + + Профили + + + + + 0 + 10 + 171 + 121 + + + + Войти + + + + + 10 + 20 + 151 + 20 + + + + Логин + + + + + + 10 + 50 + 151 + 20 + + + + QLineEdit::Password + + + Пароль + + + + + + 90 + 90 + 75 + 23 + + + + PointingHandCursor + + + Войти + + + + + + 10 + 90 + 70 + 17 + + + + PointingHandCursor + + + Показать + + + + + + + 185 + 10 + 231 + 201 + + + + + Игрок + + + + + + + 0 + 130 + 171 + 121 + + + + Зарегистрировать профиль + + + + + 10 + 20 + 151 + 20 + + + + Логин + + + + + + 10 + 50 + 151 + 20 + + + + QLineEdit::Password + + + Пароль + + + + + + 90 + 90 + 75 + 23 + + + + PointingHandCursor + + + Регистрация + + + + + + 10 + 90 + 70 + 17 + + + + PointingHandCursor + + + Показать + + + + + + false + + + + 330 + 230 + 75 + 23 + + + + PointingHandCursor + + + Выйти + + + + + + + + 20 + 300 + 411 + 31 + + + + Войдите или зарегайте профиль, чтобы играть! + + + + + + diff --git a/runCore.sh b/runCore.sh new file mode 100644 index 0000000..0f95607 --- /dev/null +++ b/runCore.sh @@ -0,0 +1,2 @@ +cd Gamecore +start gamecore.exe \ No newline at end of file diff --git a/testgame.py b/testgame.py new file mode 100644 index 0000000..398f964 --- /dev/null +++ b/testgame.py @@ -0,0 +1,109 @@ +import corelib, os, termcolor, colorama +colorama.init() + +clear = lambda: os.system('cls') +gamedata = corelib.newGame(ID=0, mines=25, fieldsize=10)['response'] +gamedata['continue'] = True +clear() + + +def getuserfield(field, gamefinnised=False): + maxFieldCoord = len(field) - 1 + # ░ свободная клетка + # █ Не открытая клетка + # F Помечена флагом + # 1..4 Мины рядом + # X Минa взорвана + # S Минa обезврежена + retfield = list() + for row in field: + X = field.index(row) + retfield.append(list()) + for cell in row: + Y = field[X].index(cell) + if ':O' in cell and cell != 'M:O': + mines = 0 + # Проверим наличие мин + upCoord = Y != 0 + downCoord = Y != maxFieldCoord + leftCoord = X != 0 + rightCoord = X != maxFieldCoord + + if upCoord and field[X][Y - 1] != cell and ':O' not in field[X][Y - 1]: + mines += 1 + if downCoord and field[X][Y + 1] != cell and ':O' not in field[X][Y + 1]: + mines += 1 + if leftCoord and field[X - 1][Y] != cell and ':O' not in field[X - 1][Y]: + mines += 1 + if rightCoord and field[X + 1][Y] != cell and ':O' not in field[X + 1][Y]: + mines += 1 + if mines == 0: + if upCoord and field[X][Y - 1] != cell: + mines = 1 + elif downCoord and field[X][Y + 1] != cell: + mines = 1 + elif leftCoord and field[X - 1][Y] != cell: + mines = 1 + elif rightCoord and field[X + 1][Y] != cell: + mines = 1 + + if mines == 0: + retfield[-1].append('░') + elif mines >= 3: + retfield[-1].append(termcolor.colored(f"{mines}", "red")) + elif mines == 2: + retfield[-1].append(termcolor.colored(f"{mines}", "yellow")) + elif mines == 1: + retfield[-1].append(termcolor.colored(f"{mines}", "green")) + elif cell == 'M:F': + if gamefinnised: + retfield[-1].append('S') + else: + retfield[-1].append('F') + elif ':F' in cell: + retfield[-1].append('F') + elif cell == 'M': + if gamefinnised: + retfield[-1].append('X') + else: + retfield[-1].append('█') + else: + retfield[-1].append('█') + + return retfield + + +clear() +while gamedata['continue']: + print('Y/X| 0123456789\n---|=============') + i = 0 + for row in getuserfield(gamedata['map'], gamefinnised=not(gamedata['continue'])): + print(f"{i}|. ", ''.join(row)) + i += 1 + + while True: + try: + X = int(input('Введите X: ')) + break + except: + pass + while True: + try: + Y = int(input('Введите Y: ')) + break + except: + pass + if input('Введите 0 если хотите поставить флаг: ') == '0': + corelib.toggleFlag(gamedata['gamesession'], X, Y) + else: + corelib.openItem(gamedata['gamesession'], X, Y) + + gamedata = corelib.getGameSession(gamedata['gamesession'])['response'] + clear() + +print('Y/X| 0123456789\n---|=============') +i = 0 +for row in getuserfield(gamedata['map'], gamefinnised=not(gamedata['continue'])): + print(f"{i}|. ", ''.join(row)) + i += 1 +print('Игра окончена!') \ No newline at end of file diff --git a/update.bat b/update.bat new file mode 100644 index 0000000..0c1dff0 --- /dev/null +++ b/update.bat @@ -0,0 +1,3 @@ +git add . +git commit -m "Up-Date repository" +git push \ No newline at end of file