Skip to content

Commit

Permalink
[TTTK-15], [TTTK-54]: Fixed a lot of bugs in AI, Client and Server
Browse files Browse the repository at this point in the history
  • Loading branch information
Timm Weber committed Mar 4, 2024
1 parent 1d37928 commit 2bea253
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 35 deletions.
2 changes: 2 additions & 0 deletions AI/ai_context.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from . import ai_strategy
from threading import Thread
import time

class AIContext():

Expand Down Expand Up @@ -27,6 +28,7 @@ def run_strategy(self):
ai_context = AIContext(weak_ai)
# run the strategy
ai_context.run_strategy()
time.sleep(3)
# create strong ai strategy: set arg to true to indicate that it is the second AI-player in the game(this is needed if AIs play against each other)
strong_ai = ai_strategy.AdvancedAIStrategy(True)
# set the strategy
Expand Down
62 changes: 38 additions & 24 deletions AI/ai_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,25 @@
from Server.gamestate import GameState
import asyncio
import random
import logging
import copy

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

class AIStrategy(ABC, GameClient):

def __init__(self, second_player: bool):

self._strength = "Placeholder"
self._good_luck_message = "Good luck!"
self._good_game_message_lost = "Good game! You played well."
self._good_game_message_won = "Good game! I'll have better luck next time."
self._good_game_message_draw = "Good game! We are evenly matched."
self._ai_uuid = "108eaa05-2b0e-4e00-a190-8856edcd56a5"
self._ai_uuid2 = "108eaa05-2b0e-4e00-a190-8856edcd56a6"
self._rulebase = RuleBase()
self._ip = "127.0.0.1"
self._port = 8765
super().__init__(self._ip, self._port, self._player)

def thread_entry(self):
asyncio.run(self.run())
Expand All @@ -27,15 +32,18 @@ async def run(self):

# The AI-UUID is hardcoded so that it can be excluded from statistics
await self.join_game()
asyncio.timeout(1)
await self.lobby_ready()
print("test")
logger.info("test")

await self._listening_task

async def join_game(self):

await self.connect()
self._listening_task = asyncio.create_task(self.listen())

await asyncio.create_task(self.join_lobby())
await self.join_lobby()



Expand All @@ -46,17 +54,18 @@ async def _message_handler(self, message_type: str):
# AI does not need this
pass
case "game/start":
print("start")
self.wish_good_luck()
logger.info("start")
if self._current_player == self._player:
await self.do_turn()
#await self.wish_good_luck()

case "game/end":
self.say_good_game()
await self.say_good_game()
await self.close()
await self._listening_task

case "game/turn":
if self._current_player.uuid == self._ai_uuid:
self.do_turn()
if self._current_player == self._player:
await self.do_turn()

case "statistics/statistics":
# AI does not need this
Expand All @@ -70,16 +79,16 @@ async def _message_handler(self, message_type: str):

return

def wish_good_luck(self):
self.chat_message(self._good_luck_message)
async def wish_good_luck(self):
await self.chat_message(self._good_luck_message)

def say_good_game(self):
async def say_good_game(self):
if self._winner.uuid == self._ai_uuid:
self.chat_message(self._good_game_message_won)
await self.chat_message(self._good_game_message_won)
elif self._winner.uuid == None:
self.chat_message(self._good_game_message_draw)
await self.chat_message(self._good_game_message_draw)
else:
self.chat_message(self._good_game_message_lost)
await self.chat_message(self._good_game_message_lost)

def get_empty_cells(self, game_status: list):
"""
Expand All @@ -101,30 +110,34 @@ async def do_turn(self):
class WeakAIStrategy(AIStrategy):

def __init__(self, second_player: bool = False):
super().__init__(second_player)
self._ai_uuid = "108eaa05-2b0e-4e00-a190-8856edcd56a5"
self._ai_uuid2 = "108eaa05-2b0e-4e00-a190-8856edcd56a6"
self._strength = "Weak"
self._good_luck_message = "Good luck! I'm still learning so please have mercy on me."
self._good_game_message_lost = "Good game! I will try to do better next time."
self._good_game_message_won = "Good game! I can't believe I won!"
self._good_game_message_draw = "Good game! I' happy I didn't lose."
self._player = Player(f"{self._strength} AI", random.randint(0, 0xFFFFFF), uuid=(self._ai_uuid if not second_player else self._ai_uuid2))
super().__init__(second_player)

async def do_turn(self):

empty_cells = self.get_empty_cells(self._game_status)
empty_cells = self.get_empty_cells(self._playfield)
move = random.randint(0, len(empty_cells) - 1)
await self.game_make_move(empty_cells[move][0], empty_cells[move][1])

class AdvancedAIStrategy(AIStrategy):

def __init__(self, second_player: bool = False):
super().__init__(second_player)
self._ai_uuid = "108eaa05-2b0e-4e00-a190-8856edcd56a5"
self._ai_uuid2 = "108eaa05-2b0e-4e00-a190-8856edcd56a6"
self._strength = "Advanced"
self._good_luck_message = "Good luck! I hope you are ready for a challenge."
self._good_game_message_lost = "Good game! I admire your skills."
self._good_game_message_won = "Good game! I hope you learned something from me."
self._good_game_message_draw = "Good game! I hope you are ready for a rematch."
self._player = Player(f"{self._strength} AI", random.randint(0, 0xFFFFFF), uuid=(self._ai_uuid if not second_player else self._ai_uuid2))
super().__init__(second_player)

async def do_turn(self):
"""
Expand All @@ -134,17 +147,18 @@ async def do_turn(self):
3. Make a random move(or maybe more complex logic)
"""

empty_cells = self.get_empty_cells(self._game_status)
empty_cells = self.get_empty_cells(self._playfield)

# Check for own winning move
for possible_move in empty_cells:
temp_gamestate = GameState()

# Make deep copy of the game state
temp_gamestate._playfield = [self._game_status[row].copy() for row in self._game_status]
temp_gamestate._playfield = copy.deepcopy(self._playfield)

temp_gamestate.set_player_position(self._player_number, possible_move)
RuleBase.check_win(temp_gamestate)
logger.info(temp_gamestate._playfield)
self._rulebase.check_win(temp_gamestate)
if temp_gamestate.winner == self._player_number:
await self.game_make_move(possible_move[0], possible_move[1])
return
Expand All @@ -154,10 +168,10 @@ async def do_turn(self):
temp_gamestate = GameState()

# Make deep copy of the game state
temp_gamestate._playfield = [self._game_status[row].copy() for row in self._game_status]
temp_gamestate._playfield = copy.deepcopy(self._playfield)

temp_gamestate.set_player_position(self._opponent_number, possible_move)
RuleBase.check_win(temp_gamestate)
self._rulebase.check_win(temp_gamestate)
if temp_gamestate.winner == self._opponent_number:
await self.game_make_move(possible_move[0], possible_move[1])
return
Expand Down
2 changes: 1 addition & 1 deletion Client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class GameClient:
_opponent_number (int): The number of the opponent in the game. 1 for the first player, 2 for the second player.
_current_player (Player): The player that is currently allowed to make a move.
_lobby_status (list[str]): The status of the lobby. Contains all players in the lobby.
_game_status (list[list[int]]): The status of the game. Contains the current playfield.
_playfield (list[list[int]]): The status of the game. Contains the current playfield.
_statistics: The statistics of the game. TODO
_chat_history (list[tuple[Player, str]]): The chat history of the game. Contains all messages sent in the game.
_winner (Player): The winner of the game. None if the game is not finished yet or it is a draw.
Expand Down
1 change: 1 addition & 0 deletions Server/gamestate.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def set_player_position(self, player: int, new_position: tuple[int, int]):
new_position (tuple[int, int]): The new position set by the player on the playfield.
"""
self._playfield[new_position[0]][new_position[1]] = player
self._current_player = 1 if player == 2 else 2

def set_winner(self, winner: int):
"""
Expand Down
21 changes: 11 additions & 10 deletions Server/websocket_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@

class Lobby:
def __init__(self, admin:Player, port: int = 8765) -> None:
self._players = {admin.uuid : admin}
self._players = {}
#self._players = {admin.uuid : admin}
self._game = None
self._inprogress = False
self._port = port
Expand Down Expand Up @@ -67,7 +68,7 @@ async def handler(self, websocket):
"players": [player.as_dict() for player in self._players.values()],
}))

if all([player.ready for player in self._players.values()]) and len(self._players) == 2:
if all([player.ready for player in self._players.values()]) and len(self._connections) == 2:
# TODO add error messages for why game cant start with not enough or too many ready players
# all players are ready, start the game
rulebase = RuleBase()
Expand All @@ -84,32 +85,32 @@ async def handler(self, websocket):
case "game/make_move":
# check if move can be made
if not self._inprogress:
await websocket.send(json.dumps({"message_type": "game/error", "error": "Game not in progress"}))
await websocket.send(json.dumps({"message_type": "game/error", "error_message": "Game not in progress"}))
break
if message_json["player_uuid"] != self._game.current_player_uuid:
await websocket.send(json.dumps({"message_type": "game/error", "error": "Not your turn"}))
await websocket.send(json.dumps({"message_type": "game/error", "error_message": "Not your turn"}))
break

# make move, catch illegal move
try:
self._game.move(self._players(message_json["player_uuid"]), message_json["move"])
self._game.move(self._game.players.index(self._players[message_json["player_uuid"]]), (message_json["move"]["x"], message_json["move"]["y"]))
except ValueError as e:
await websocket.send(json.dumps({"message_type": "game/error", "error": str(e)}))
await websocket.send(json.dumps({"message_type": "game/error", "error_message": str(e)}))

# check for winning state
if self._game.finished:
if self._game.state.finished:
websockets.broadcast(self._connections, json.dumps({
"message_type": "game/end",
"winner_uuid": self._game.winner.uuid,
"final_playfiled": self._game.playfield,
"winner_uuid": self._game.state.winner.uuid,
"final_playfiled": self._game.state.playfield,
}))
self._inprogress = False

# announce new game state
else:
websockets.broadcast(self._connections, json.dumps({
"message_type": "game/turn",
"updated_playfield": self._game.playfield,
"updated_playfield": self._game.state.playfield,
"next_player_uuid": self._game.current_player_uuid,
}))

Expand Down

0 comments on commit 2bea253

Please sign in to comment.