Skip to content

Commit

Permalink
[FIX] Small bugfixes for demo
Browse files Browse the repository at this point in the history
  • Loading branch information
l9kd1 committed Aug 1, 2023
1 parent 6b53731 commit e86f433
Show file tree
Hide file tree
Showing 14 changed files with 212 additions and 49 deletions.
2 changes: 1 addition & 1 deletion src/abalone.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def __init__(self, mimics: type[Player], *args, **kwargs) -> None:
async def play(self, current_state: GameState) -> Action:
while True:
data = json.loads(await EventMaster.get_instance().wait_for_event("interact"))
action = current_state.convert_light_action_to_action(data["from"], data["to"])
action = current_state.convert_light_action_to_action(data)
if action in current_state.get_possible_actions():
break
else:
Expand Down
160 changes: 160 additions & 0 deletions src/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import asyncio
import sys
import argparse
import subprocess
import os
import json
from seahorse.examples.abalone import alpha_player_abalone
from seahorse.examples.abalone.board_abalone import BoardAbalone
from seahorse.examples.abalone.game_state_abalone import GameStateAbalone
from seahorse.examples.abalone.master_abalone import MasterAbalone
from seahorse.examples.tictactoe.alpha_player_tictac import MyPlayer as AlphaPlayerTictac
from seahorse.examples.tictactoe.board_tictac import BoardTictac
from seahorse.examples.tictactoe.game_state_tictac import GameStateTictac
from seahorse.examples.tictactoe.master_tictac import MasterTictac
from seahorse.examples.tictactoe.player_tictac import PlayerTictac
from seahorse.game.action import Action
from seahorse.game.game_layout.board import Piece
from seahorse.game.game_state import GameState
from seahorse.game.io_stream import EventMaster
from seahorse.player.player import Player
from seahorse.player.proxies import LocalPlayerProxy, RemotePlayerProxy, InteractivePlayerProxy
from seahorse.examples.abalone.random_player_abalone import MyPlayer as RandomPlayerAbalone
from seahorse.examples.abalone.alpha_player_abalone import MyPlayer as AlphaPlayerAbalone

W = 1
B = 2
def open_webpage(url):
try:
os.startfile(url)
except AttributeError:
try:
subprocess.call(['open', url])
except:
print('Could not open URL')

def tictactoe(args):
if args.remote:
pass
else:
open_webpage('/'.join(["file://"]+os.path.abspath(__file__).split("/")[:-1]+["seahorse/execution/web/examples/tictactoe/index.html"]))
player1 = LocalPlayerProxy(AlphaPlayerTictac("X", name="louis"),gs=GameStateTictac)
player2 = InteractivePlayerProxy(PlayerTictac("O", name="pierre"))

list_players = [player1, player2]
init_scores = {player1.get_id(): 0, player2.get_id(): 0}
init_rep = BoardTictac(env={}, dim=[3, 3])
initial_game_state = GameStateTictac(
scores=init_scores, next_player=player1, players=list_players, rep=init_rep)

master = MasterTictac(
name="Tic-Tac-Toe",
initial_game_state=initial_game_state,
players_iterator=list_players,
log_file="log.txt",
port=args.port,
hostname=args.address,
n_listeners=args.listeners
)
master.record_game()

def abalone(args):
if args.remote:
open_webpage('/'.join(["file://"]+os.path.abspath(__file__).split("/")[:-1]+["seahorse/execution/web/examples/abalone/interactive/index.html"]))

player1 = LocalPlayerProxy(AlphaPlayerAbalone(piece_type="W", name= "marcel"),masterless=True,gs=GameStateAbalone)
asyncio.new_event_loop().run_until_complete(player1.listen(keep_alive=True,master_address=f"http://{args.address}:{args.port}"))
else:
open_webpage('/'.join(["file://"]+os.path.abspath(__file__).split("/")[:-1]+["seahorse/execution/web/examples/abalone/interactive/index.html"]))

player2 = RemotePlayerProxy(mimics=RandomPlayerAbalone,piece_type="W",name="marcel")
player1 = InteractivePlayerProxy(AlphaPlayerAbalone(piece_type="B"))

list_players = [player1, player2]
init_scores = {player1.get_id(): 0, player2.get_id(): 0}
print("init_scores", init_scores)
dim = [17, 9]
env = {}
initial_board = [
[0, 0, 0, 0, 2, 0, 0, 0, 0],
[0, 0, 0, 3, 0, 3, 0, 0, 0],
[0, 0, 2, 0, 2, 0, 3, 0, 0],
[0, 3, 0, 1, 0, 2, 0, 3, 0],
[2, 0, 1, 0, 1, 0, 3, 0, 3],
[0, 2, 0, 2, 0, 3, 0, 3, 0],
[3, 0, 1, 0, 2, 0, 3, 0, 3],
[0, 2, 0, 2, 0, 3, 0, 3, 0],
[3, 0, 3, 0, 3, 0, 3, 0, 3],
[0, 3, 0, 3, 0, 1, 0, 1, 0],
[3, 0, 3, 0, 1, 0, 2, 0, 3],
[0, 3, 0, 3, 0, 1, 0, 1, 0],
[3, 0, 3, 0, 2, 0, 2, 0, 1],
[0, 3, 0, 1, 0, 2, 0, 3, 0],
[0, 0, 3, 0, 1, 0, 1, 0, 0],
[0, 0, 0, 3, 0, 3, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0],
]
# MARGUERITE
# initial_board = [
# [0, 0, 0, 0, 1, 0, 0, 0, 0],
# [0, 0, 0, 1, 0, 1, 0, 0, 0],
# [0, 0, 3, 0, 1, 0, 3, 0, 0],
# [0, 2, 0, 1, 0, 1, 0, 3, 0],
# [2, 0, 2, 0, 1, 0, 3, 0, 3],
# [0, 2, 0, 3, 0, 3, 0, 3, 0],
# [2, 0, 2, 0, 3, 0, 3, 0, 3],
# [0, 2, 0, 3, 0, 3, 0, 3, 0],
# [3, 0, 3, 0, 3, 0, 3, 0, 3],
# [0, 3, 0, 3, 0, 3, 0, 2, 0],
# [3, 0, 3, 0, 3, 0, 2, 0, 2],
# [0, 3, 0, 3, 0, 3, 0, 2, 0],
# [3, 0, 3, 0, 1, 0, 2, 0, 2],
# [0, 3, 0, 1, 0, 1, 0, 2, 0],
# [0, 0, 3, 0, 1, 0, 3, 0, 0],
# [0, 0, 0, 1, 0, 1, 0, 0, 0],
# [0, 0, 0, 0, 1, 0, 0, 0, 0],
# ]

for i in range(dim[0]):
for j in range(dim[1]):
if initial_board[i][j] == W:
env[(i, j)] = Piece(piece_type=player1.get_piece_type(), owner=player1)
elif initial_board[i][j] == B:
env[(i, j)] = Piece(piece_type=player2.get_piece_type(), owner=player2)
init_rep = BoardAbalone(env=env, dim=dim)
initial_game_state = GameStateAbalone(
scores=init_scores, next_player=player1, players=list_players, rep=init_rep, step=0
)

master = MasterAbalone(
name="Abalone",
initial_game_state=initial_game_state,
players_iterator=list_players,
log_file="log.txt",
port=args.port,
hostname=args.address,
n_listeners=args.listeners
)
master.record_game()

if __name__=="__main__":

parser = argparse.ArgumentParser(
prog='Launcher',
description='What the program does',
epilog='Text at the bottom of help')
parser.add_argument('-p','--port',required=True,type=int)
parser.add_argument('-a','--address',required=True)
parser.add_argument('-r','--remote',required=False,action='store_true')
parser.add_argument('-l','--listeners',required=False,type=int)
parser.add_argument('-g','--game',required=True,choices=['tictactoe','abalone','avalam'])
args=parser.parse_args()

if args.game=="tictactoe":
tictactoe(args)
elif args.game=="abalone":
abalone(args)




5 changes: 3 additions & 2 deletions src/seahorse/examples/abalone/game_state_abalone.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,8 @@ def generate_possible_actions(self) -> Set[Action]:
}
return poss_actions

def convert_light_action_to_action(self,src:tuple[int, int], dst:tuple[int, int]) -> Action :
def convert_light_action_to_action(self,data) -> Action :
src,dst=data['from'],data['to']
current_game_state = self
b = current_game_state.get_rep().get_env()
d = current_game_state.get_rep().get_dimensions()
Expand Down Expand Up @@ -277,5 +278,5 @@ def to_json(self) -> str:
@classmethod
def from_json(cls,data:str,*,next_player:PlayerAbalone=None) -> Serializable:
d = json.loads(data)
return cls(**{**d,"scores":{int(k):v for k,v in d["scores"].items()},"players":[PlayerAbalone.from_json(json.dumps(x)) if x!="self" else next_player for x in d["players"]],"next_player":next_player,"rep":BoardAbalone.from_json(json.dumps(d["rep"]))})
return cls(**{**d,"scores":{int(k):v for k,v in d["scores"].items()},"players":[PlayerAbalone.from_json(json.dumps(x)) if not isinstance(x,str) else next_player for x in d["players"]],"next_player":next_player,"rep":BoardAbalone.from_json(json.dumps(d["rep"]))})

17 changes: 2 additions & 15 deletions src/seahorse/examples/abalone/master_abalone.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,8 @@ class MasterAbalone(GameMaster):
log_file (str): Name of the log file
"""

def __init__(
self, name: str, initial_game_state: GameState, players_iterator: Iterable[Player], log_file: str, port: int = 8080
) -> None:
"""
Initialize the MasterAbalone instance.
Args:
name (str): Name of the game
initial_game_state (GameState): Initial state of the game
players_iterator (Iterable): An iterable for the players_iterator, ordered according to the playing order.
If a list is provided, a cyclic iterator is automatically built
log_file (str): Name of the log file
"""
super().__init__(name, initial_game_state, players_iterator, log_file, port)

def __init__(self, name: str, initial_game_state: GameState, players_iterator: Iterable[Player], log_file: str, port: int = 8080, hostname: str = "localhost", n_listeners: int = 4) -> None:
super().__init__(name, initial_game_state, players_iterator, log_file, port, hostname, n_listeners)
def compute_winner(self, scores: Dict[int, float]) -> List[Player]:
"""
Computes the winners of the game based on the scores.
Expand Down
2 changes: 1 addition & 1 deletion src/seahorse/examples/avalam/game_state_avalam.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,4 @@ def to_json(self) -> str:
@classmethod
def from_json(cls,data:str,*,next_player:PlayerAvalam=None) -> Serializable:
d = json.loads(data)
return cls(**{**d,"scores":{int(k):v for k,v in d["scores"].items()},"players":[PlayerAvalam.from_json(x) if json.loads(x)!="self" else next_player for x in d["players"]],"next_player":next_player,"rep":BoardAvalam.from_json(json.dumps(d["rep"]))})
return cls(**{**d,"scores":{int(k):v for k,v in d["scores"].items()},"players":[PlayerAvalam.from_json(x) if not isinstance(x,str) else next_player for x in d["players"]],"next_player":next_player,"rep":BoardAvalam.from_json(json.dumps(d["rep"]))})
2 changes: 1 addition & 1 deletion src/seahorse/examples/mancala/game_state_mancala.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,5 +219,5 @@ def to_json(self) -> str:
@classmethod
def from_json(cls,data:str,*,next_player:PlayerMancala=None) -> Serializable:
d = json.loads(data)
return cls(**{**d,"scores":{int(k):v for k,v in d["scores"].items()},"players":[PlayerMancala.from_json(json.dumps(x)) if x!="self" else next_player for x in d["players"]],"next_player":next_player,"rep":BoardMancala.from_json(json.dumps(d["rep"]))})
return cls(**{**d,"scores":{int(k):v for k,v in d["scores"].items()},"players":[PlayerMancala.from_json(json.dumps(x)) if not isinstance(x,str) else next_player for x in d["players"]],"next_player":next_player,"rep":BoardMancala.from_json(json.dumps(d["rep"]))})

2 changes: 1 addition & 1 deletion src/seahorse/examples/tictactoe/game_state_tictac.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,4 +206,4 @@ def to_json(self) -> dict:
@classmethod
def from_json(cls,data:str,*,next_player:PlayerTictac=None) -> Serializable:
d = json.loads(data)
return cls(**{**d,"scores":{int(k):v for k,v in d["scores"].items()},"players":[PlayerTictac.from_json(json.dumps(x)) if x!="self" else next_player for x in d["players"]],"next_player":next_player,"rep":BoardTictac.from_json(json.dumps(d["rep"]))})
return cls(**{**d,"scores":{int(k):v for k,v in d["scores"].items()},"players":[PlayerTictac.from_json(json.dumps(x)) if not isinstance(x,str) else next_player for x in d["players"]],"next_player":next_player,"rep":BoardTictac.from_json(json.dumps(d["rep"]))})
18 changes: 3 additions & 15 deletions src/seahorse/examples/tictactoe/master_tictac.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,9 @@ class MasterTictac(GameMaster):
log_file (str): The name of the log file.
"""

def __init__(
self, name: str, initial_game_state: GameState, players_iterator: Iterable[Player], log_file: str, port: int = 8080
) -> None:
"""
Initializes a new instance of the MasterTictac class.
Args:
name (str): The name of the game.
initial_game_state (GameState): The initial state of the game.
players_iterator (Iterable[Player]): An iterable for the players_iterator, ordered according
to the playing order.
log_file (str): The name of the log file.
"""
super().__init__(name, initial_game_state, players_iterator, log_file, port)

def __init__(self, name: str, initial_game_state: GameState, players_iterator: Iterable[Player], log_file: str, port: int = 8080, hostname: str = "localhost", n_listeners: int = 4) -> None:
super().__init__(name, initial_game_state, players_iterator, log_file, port, hostname, n_listeners=n_listeners)

def compute_winner(self, scores: Dict[int, float]) -> List[Player]:
"""
Computes the winners of the game based on the scores.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ $(document).ready(function () {
var index = -1;
const logElement = document.getElementById("log");
var play = false;
const socket = io("ws://10.200.37.65:16001");
const socket = io("ws://localhost:16001");


socket.on("play", (...args) => {
Expand Down Expand Up @@ -291,12 +291,17 @@ $(document).ready(function () {

for(i = 0; i < players.length; i++) {
//console.log(players[i]);
if(typeof players[i] === 'string' || players[i] instanceof String){
players[i]={"id":parseInt(players[i])}
pt = Object.entries(board).filter(e=>e[1]["owner_id"]==players[i].id)[0][1].piece_type
console.log(pt)
players[i]["piece_type"]=pt
}

if(players[i].piece_type == "W") score_w = -scores[players[i].id];
if(players[i].piece_type == "B") score_b = -scores[players[i].id];
}
real_scores = {"W":score_w, "B":score_b}
//console.log(real_scores);

const gridData = [
[0, 0, 3, 3, 3, 3, 3, 0, 0],
Expand Down
4 changes: 2 additions & 2 deletions src/seahorse/execution/web/examples/tictactoe/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ $(document).ready(function () {
// create a div
var div = document.createElement("div");
if (type == "X"){
div.innerHTML = "";
div.innerHTML = "&#x2715";
div.style.color = "red";
}
else{
div.innerHTML = "";
div.innerHTML = "&#x25EF;";
div.style.color = "blue";
}
document.getElementById("c"+id).appendChild(div);
Expand Down
11 changes: 6 additions & 5 deletions src/seahorse/game/io_stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class EventMaster:
__instance = None

@staticmethod
def get_instance(n_clients:int=1,game_state:type=Serializable,port: int = 8080) -> EventMaster:
def get_instance(n_clients:int=1,game_state:type=Serializable,port: int = 8080,hostname: str="localhost") -> EventMaster:
"""Gets the instance object
Args:
Expand All @@ -106,10 +106,10 @@ def get_instance(n_clients:int=1,game_state:type=Serializable,port: int = 8080)
object: _description_
"""
if EventMaster.__instance is None:
EventMaster(n_clients=n_clients,game_state=game_state, port=port)
EventMaster(n_clients=n_clients,game_state=game_state, port=port, hostname=hostname)
return EventMaster.__instance

def __init__(self,n_clients,game_state,port):
def __init__(self,n_clients,game_state,port,hostname):
if EventMaster.__instance is not None:
msg = "Trying to initialize multiple instances of EventMaster, this is forbidden to avoid side-effects.\n Call EventMaster.get_instance() instead."
raise NotImplementedError(msg)
Expand All @@ -124,6 +124,7 @@ def __init__(self,n_clients,game_state,port):
self.__events = {}
self.__game_state = game_state
self.port = port
self.hostname = hostname

# Standard python-socketio server
self.sio = socketio.AsyncServer(async_mode="aiohttp", async_handlers=True, cors_allowed_origins="*")
Expand Down Expand Up @@ -260,12 +261,12 @@ def start(self, task: Callable[[None], None], listeners: list[EventSlave]) -> No
# Sets the runner up and starts the tcp server
self.event_loop.run_until_complete(self.runner.setup())
#print(self.port)
site = web.TCPSite(self.runner, "10.200.37.65", self.port)
site = web.TCPSite(self.runner, self.hostname, self.port)
self.event_loop.run_until_complete(site.start())

async def stop():
for x in listeners:
await x.listen(master_address="http://10.200.37.65:"+str(self.port), keep_alive=False)
await x.listen(master_address=f"http://{self.hostname}:{str(self.port)}", keep_alive=False)

# Waiting for all listeners
await self._wait_for_connexion()
Expand Down
11 changes: 9 additions & 2 deletions src/seahorse/game/master.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,14 @@ class GameMaster:
"""

def __init__(
self, name: str, initial_game_state: GameState, players_iterator: Iterable[Player], log_file: str, port: int =8080
self,
name: str,
initial_game_state: GameState,
players_iterator: Iterable[Player],
log_file: str,
port: int =8080,
hostname: str ="localhost",
n_listeners: int =4
) -> None:
"""
Initializes a new instance of the GameMaster class.
Expand All @@ -48,7 +55,7 @@ def __init__(
self.log_file = log_file
self.players_iterator = cycle(players_iterator) if isinstance(players_iterator, list) else players_iterator
next(self.players_iterator)
self.emitter = EventMaster.get_instance(4,initial_game_state.__class__,port=port)
self.emitter = EventMaster.get_instance(n_listeners,initial_game_state.__class__,port=port,hostname=hostname)

async def step(self) -> GameState:
"""
Expand Down
17 changes: 16 additions & 1 deletion src/seahorse/player/proxies.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def __eq__(self, __value: object) -> bool:
return hash(self) == hash(__value)

def to_json(self) -> str:
return "self"
return str(self.wrapped_id)


class LocalPlayerProxy(Serializable,EventSlave):
Expand Down Expand Up @@ -127,3 +127,18 @@ def __str__(self) -> str:

def to_json(self) -> dict:
return self.wrapped_player.to_json()

class InteractivePlayerProxy(LocalPlayerProxy):
def __init__(self, mimics: type[Player], *args, **kwargs) -> None:
super().__init__(mimics, *args, **kwargs)
self.wrapped_player.player_type = "interactive"

async def play(self, current_state: GameState) -> Action:
while True:
data = json.loads(await EventMaster.get_instance().wait_for_event("interact"))
action = current_state.convert_light_action_to_action(data)
if action in current_state.get_possible_actions():
break
else:
await EventMaster.get_instance().sio.emit("ActionNotPermitted",None)
return action
Loading

0 comments on commit e86f433

Please sign in to comment.