Skip to content

Commit

Permalink
Merge branch 'main' into docs_new
Browse files Browse the repository at this point in the history
  • Loading branch information
Petzys authored Mar 20, 2024
2 parents 8e925c8 + 13603f3 commit 3006ffb
Show file tree
Hide file tree
Showing 26 changed files with 815 additions and 273 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,4 @@ l10n/
launch.json
venv
*.db
profiles.json
Client/Data/profiles.json
3 changes: 3 additions & 0 deletions AI/ai_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ def run_strategy(self):
thread = Thread(target=self._strategy.thread_entry, daemon=True)
thread.start()
return thread

def get_uuid(self):
return self._strategy.get_uuid()

if __name__ == "__main__":
weak_ai = ai_strategy.WeakAIStrategy()
Expand Down
4 changes: 4 additions & 0 deletions AI/ai_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import random
import logging
import copy
from uuid import UUID

# Set up logging
logging.basicConfig(level=logging.INFO)
Expand Down Expand Up @@ -35,6 +36,9 @@ def post_init(self):
#needs to be called by inheriting classes at the end of their __init__ function
super().__init__(self._ip, self._port, self._player)

def get_uuid(self):
return UUID(self._current_uuid)

def thread_entry(self):
"""
Entry point for the AI thread. Run the AI in asyncio.
Expand Down
19 changes: 16 additions & 3 deletions Client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def __init__(self, ip:str, port:int, player:Player) -> None:
self._player: Player = player
self._player_number: int = None
self._symbol: str = None
self._kicked: bool = False

# Opponent info
self._opponent: Player = None
Expand All @@ -80,7 +81,14 @@ def __init__(self, ip:str, port:int, player:Player) -> None:
self._json_schema = json.load(f)

async def connect(self):
self._websocket = await connect(f"ws://{self._ip}:{str(self._port)}")
# Try 5 times to connect to the server
for i in range(5):
try:
self._websocket = await connect(f"ws://{self._ip}:{str(self._port)}")
break
except Exception as e:
logger.error(f"Could not connect to server. Attempt {i+1}/5. Retrying in 0.5 seconds...")
await asyncio.sleep(0.5)

@classmethod
async def create_game(cls, player: Player, port:int = 8765) -> tuple[GameClient, asyncio.Task, Thread]:
Expand Down Expand Up @@ -152,6 +160,10 @@ async def listen(self):

if message_type == "game/end":
await self.terminate()
break
elif self._kicked:
await self.close()
break

def get_player_by_uuid(self, uuid:str) -> Player:
for player in self._lobby_status:
Expand Down Expand Up @@ -216,8 +228,8 @@ async def _preprocess_message(self, message:str) -> str:
await self.join_lobby()
case "lobby/kick":
if str(self._player.uuid) == message_json["kick_player_uuid"]:
logger.info("You have been kicked from the lobby.")
await self.close()
logger.info("You have been kicked from the lobby. Closing after processing the message...")
self._kicked = True
case _:
logger.error(f"Unknown message type: {message_json['message_type']}")
raise ValidationError("Game start message received, but lobby does not contain 2 players. This should not happen and should be investigated.")
Expand Down Expand Up @@ -305,6 +317,7 @@ async def chat_message(self, message:str):

async def close(self):
await self._websocket.close()
exit()

async def terminate(self):
msg = {
Expand Down
138 changes: 39 additions & 99 deletions Client/profile_save.py
Original file line number Diff line number Diff line change
@@ -1,120 +1,60 @@
import json
from os.path import exists
import os
from Server.player import Player

# Path to the profiles.json file
path = ('../json_schema/profiles.json')
path=os.path.abspath('Client/Data/profiles.json')

class Profile:
"""
This class is used to handle the profiles.json file. It is used to get, set, and add profiles to the file.
"""
def check_file(self):
"""
This method checks if the file exists
:return: True if the file exists, False if it does not
"""
if exists(path):
print("found")
return True
else:
print("not found")
return False

def get_profiles(self):
@staticmethod
def get_profiles():
"""
This method returns all the profiles from the file
:return: An array of all profiles
"""
if self.check_file():
Profile._check_folder()
if exists(path):
with open(path, 'r') as file:
data = json.load(file)
return data
if not data:
return [], 0
output = []
profile_data = data[0]
selected = data[1]
for profile in profile_data:
output.append(Player.from_dict(profile))
return output, selected
else:
return None
def get_profile(self,profile_uuid):
"""
This method returns a profile by its uuid
:param profile_uuid:
:return: profile matching given uuid
"""
if self.check_file():
try:
with open(path, 'r') as file:
data = json.load(file)
for profile in data:
if profile["profile_uuid"] == profile_uuid:
return profile
except:
print("json error: Make sure profiles.json is formatted correctly")
return None
def set_profile(self, profile_uuid , profile_name = None, profile_color = None):
"""
This method sets the profile name and/or color by the uuid
:param profile_uuid:
:param profile_name: *optional*
:param profile_color: *optional*
"""
if self.check_file():
try:
with open(path, 'r+') as file:
data = json.load(file)
for profile in data:
if profile["profile_uuid"] == profile_uuid:
if profile_name != None:
profile["profile_name"] = profile_name
if profile_color != None:
profile["profile_color"] = profile_color
break
with open(path, 'w') as file:
json.dump(data, file)
except:
print("json error: Make sure profiles.json is formatted correctly")
return None
def get_profile_by_name(self, profile_name):
if self.check_file():
"""
This method returns a profile by its name
:param profile_name:
:return: profile matching given name
"""
try:
with open(path, 'r') as file:
data = json.load(file)
for profile in data:
if profile["profile_name"] == profile_name:
return profile
except:
print("json error: Make sure profiles.json is formatted correctly")
return None
return [], None

def add_new_profile(self, profile_name, profile_uuid, profile_color):
@staticmethod
def set_profiles(players: list, selected: int):
Profile._check_folder()
with open(path, 'w+') as file:
entry = []
for player in players:
entry.append(player.as_dict())
json.dump([entry, selected], file)

@staticmethod
def delete_all_profiles():
"""
This method adds a new profile to the file
:param profile_name:
:param profile_uuid:
:param profile_color:
This method deletes all profiles
"""
if self.check_file():
entry = {"profile_name": profile_name, "profile_uuid": profile_uuid, "profile_color": profile_color}
try:
with open(path, 'r+') as file:
data = json.load(file)
file.seek(0)
data.append(entry)
json.dump(data, file)
file.truncate()
except:
print("json error: Make sure profiles.json is formatted correctly")

else:
Profile._check_folder()
if exists(path):
with open(path, 'w') as file:
entry = [{"profile_name": profile_name, "profile_uuid": profile_uuid, "profile_color": profile_color}]
json.dump(entry, file)
file.write("[]")

#Testing
#profile = Profile()
#profile.add_new_profile("test", "test", "test")
#print(profile.get_profiles())
#print(profile.get_profile("test"))
#profile.set_profile("test", "test2", "test3")
#print(profile.get_profiles())
@staticmethod
def _check_folder():
dir = os.path.abspath('Client/Data/')
if not os.path.exists(os.path.abspath(dir)):
try:
os.makedirs(os.path.abspath(dir))
except OSError:
raise OSError(f"Creation of the directory {dir} failed")
18 changes: 18 additions & 0 deletions Client/test_profile_save.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import unittest
from Client.profile_save import Profile
from Server.player import Player
from uuid import uuid4

class TestProfileSave(unittest.TestCase):

def setUp(self):
self.player1 = Player("test", 0, uuid4(), False)
self.player2 = Player("test2", 0, uuid4(), False)
Profile.delete_all_profiles()

def test_all(self):
data = ([self.player1, self.player2], 0)
Profile.set_profiles(data[0], data[1])
self.assertEqual(Profile.get_profiles(), data)
Profile.delete_all_profiles()
self.assertEqual(Profile.get_profiles(), ([], 0))
Loading

0 comments on commit 3006ffb

Please sign in to comment.