Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
Menteur
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
PLN
Menteur
Commits
52760898
Unverified
Commit
52760898
authored
Apr 14, 2020
by
PLN (Algolia)
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
refactor(Lobby/model): metadata, pydantic models
parent
f3c9c952
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
102 additions
and
69 deletions
+102
-69
lobby.py
server/game/lobby.py
+38
-10
card.py
server/model/card.py
+4
-5
deck.py
server/model/deck.py
+1
-2
hand.py
server/model/hand.py
+4
-5
hands.py
server/model/hands.py
+34
-26
known.py
server/model/known.py
+6
-6
players.py
server/model/players.py
+6
-5
test_lobby.py
server/test/test_lobby.py
+5
-8
ws.py
server/ws.py
+4
-2
No files found.
server/game/lobby.py
View file @
52760898
from
collections
import
defaultdict
import
json
from
typing
import
List
,
Dict
from
typing
import
List
,
Dict
,
Optional
from
pydantic.main
import
BaseModel
from
socketio
import
AsyncServer
from
socketio
import
AsyncServer
from
server.game.manager
import
ClientManager
from
server.game.manager
import
ClientManager
...
@@ -9,14 +10,20 @@ from server.model.game import Game
...
@@ -9,14 +10,20 @@ from server.model.game import Game
from
server.model.players
import
Player
,
Announce
from
server.model.players
import
Player
,
Announce
class
Metadata
(
BaseModel
):
ready
:
bool
=
False
sid
:
str
=
""
last_announce
:
Optional
[
Announce
]
=
None
class
LobbyManager
(
ClientManager
):
class
LobbyManager
(
ClientManager
):
""" A ClientManager that handles a lobby, then orchestrates games.
""" A ClientManager that handles a lobby, then orchestrates games.
"""
"""
def
__init__
(
self
,
sio
:
AsyncServer
):
def
__init__
(
self
,
sio
:
AsyncServer
):
super
()
.
__init__
()
super
()
.
__init__
()
self
.
l
ast_announces
:
Dict
[
Player
,
Announce
]
=
{}
self
.
l
obby
:
Dict
[
str
,
Player
]
=
{}
self
.
lobby
:
Dict
[
Player
,
bool
]
=
{}
self
.
metadata
:
Dict
[
str
,
Metadata
]
=
{}
self
.
games
:
List
[
Game
]
=
[]
self
.
games
:
List
[
Game
]
=
[]
self
.
sio
=
sio
self
.
sio
=
sio
...
@@ -26,28 +33,49 @@ class LobbyManager(ClientManager):
...
@@ -26,28 +33,49 @@ class LobbyManager(ClientManager):
@property
@property
def
players_ready
(
self
):
def
players_ready
(
self
):
return
[
k
for
k
,
v
in
self
.
lobby
.
items
()
if
v
]
return
[
self
.
lobby
[
k
]
for
k
,
m
in
self
.
metadata
.
items
()
if
m
.
ready
]
async
def
add_player
(
self
,
player
:
Player
,
is_ready
:
bool
=
False
)
->
None
:
async
def
add_player
(
self
,
player
:
Player
,
is_ready
:
bool
=
False
)
->
str
:
self
.
lobby
[
player
]
=
is_ready
self
.
lobby
[
player
.
name
]
=
player
self
.
metadata
[
player
.
name
]
=
Metadata
(
ready
=
is_ready
)
print
(
f
"Added {player} to a lobby with {len(self.lobby)} players."
)
print
(
f
"Added {player} to a lobby with {len(self.lobby)} players."
)
await
self
.
sio
.
emit
(
'messageChannel'
,
"PONG"
)
return
f
"Bienvenu, {player}! Il y a {len(self.lobby)} joueurs en ligne."
def
wants_to_play
(
self
,
player
:
Player
):
def
wants_to_play
(
self
,
player
:
Player
):
self
.
lobby
[
player
]
=
True
self
.
metadata
[
player
.
name
]
.
ready
=
True
print
(
f
"{player} ready to play! ({self.nb_ready} people ready)"
)
print
(
f
"{player} ready to play! ({self.nb_ready} people ready)"
)
def
announces
(
self
,
player
:
Player
,
announce
:
Announce
):
def
announces
(
self
,
player
:
Player
,
announce
:
Announce
):
# FIXME: Call this message on incoming "ANNOUNCE"
# FIXME: Call this message on incoming "ANNOUNCE"
self
.
last_announces
[
player
]
=
announce
self
.
metadata
[
player
.
name
]
.
last_announce
=
announce
print
(
f
"{player} ready to play! ({self.nb_ready} people ready)"
)
print
(
f
"{player} ready to play! ({self.nb_ready} people ready)"
)
def
start_game
(
self
,
max_players
=
2
):
def
start_game
(
self
,
max_players
=
2
):
players
=
self
.
players_ready
[:
max_players
]
players
=
self
.
players_ready
[:
max_players
]
self
.
games
.
append
(
Game
(
players
,
manager
=
self
))
self
.
games
.
append
(
Game
(
players
,
manager
=
self
))
for
p
in
players
:
self
.
metadata
[
p
.
name
]
.
ready
=
False
print
(
f
"Game started : {' vs '.join([p.name for p in players])}"
)
print
(
f
"Game started : {' vs '.join([p.name for p in players])}"
)
def
send
(
self
,
to
:
Player
,
message
:
MessageToPlayer
,
extra
=
None
):
def
send
(
self
,
to
:
Player
,
message
:
MessageToPlayer
,
extra
=
None
):
self
.
sio
.
send
(
message
)
self
.
sio
.
send
(
message
)
pass
pass
def
handle_message
(
self
,
sid
,
data
):
sender
:
Optional
[
Player
]
=
None
sanitized
=
str
(
data
)
print
(
f
"Lobby| Received message from {sid}: {data}."
)
for
player
in
self
.
players
:
if
player
.
name
in
sanitized
:
sender
=
player
if
sender
:
print
(
f
"Lobby| Found sender: {sender.name}"
)
message
=
MessageToPlayer
.
Waiting
extras
=
[
p
.
name
for
p
in
self
.
players
]
body
=
{
"message"
:
message
.
name
}
if
extras
:
body
[
"extras"
]
=
extras
return
json
.
dumps
(
body
)
server/model/card.py
View file @
52760898
from
dataclasses
import
dataclass
from
enum
import
Enum
from
enum
import
Enum
from
typing
import
Optional
from
typing
import
Optional
from
pydantic.main
import
BaseModel
class
Value
(
Enum
):
class
Value
(
Enum
):
Two
=
1
Two
=
1
...
@@ -26,8 +27,7 @@ class Color(Enum):
...
@@ -26,8 +27,7 @@ class Color(Enum):
Diamonds
=
"♦"
Diamonds
=
"♦"
@dataclass
(
frozen
=
True
)
class
Card
(
BaseModel
):
class
Card
:
value
:
Value
value
:
Value
color
:
Optional
[
Color
]
=
None
color
:
Optional
[
Color
]
=
None
...
@@ -57,4 +57,4 @@ def lowest_value_and_rest():
...
@@ -57,4 +57,4 @@ def lowest_value_and_rest():
otherValues
=
list
(
Value
)
otherValues
=
list
(
Value
)
otherValues
.
remove
(
lowValue
)
otherValues
.
remove
(
lowValue
)
return
lowValue
,
otherValues
return
lowValue
,
otherValues
\ No newline at end of file
server/model/deck.py
View file @
52760898
from
random
import
randrange
,
shuffle
from
random
import
randrange
,
shuffle
from
typing
import
List
from
server.model.card
import
Card
,
Value
,
Color
from
server.model.card
import
Card
,
Value
,
Color
...
@@ -7,7 +6,7 @@ from server.model.card import Card, Value, Color
...
@@ -7,7 +6,7 @@ from server.model.card import Card, Value, Color
class
Deck
:
class
Deck
:
def
__init__
(
self
,
cards
=
None
):
def
__init__
(
self
,
cards
=
None
):
if
cards
is
None
:
if
cards
is
None
:
cards
=
[
Card
(
v
,
c
)
for
v
in
Value
for
c
in
Color
]
cards
=
[
Card
(
v
alue
=
v
,
color
=
c
)
for
v
in
Value
for
c
in
Color
]
else
:
else
:
print
(
"Deck init with cards:"
,
cards
,
len
(
cards
))
print
(
"Deck init with cards:"
,
cards
,
len
(
cards
))
self
.
cards
=
cards
self
.
cards
=
cards
...
...
server/model/hand.py
View file @
52760898
from
collections
import
Counter
from
collections
import
Counter
from
typing
import
List
from
typing
import
List
from
pydantic.main
import
BaseModel
from
server.model.card
import
Card
,
Value
from
server.model.card
import
Card
,
Value
class
Hand
:
class
Hand
(
BaseModel
):
def
__init__
(
self
,
cards
:
List
[
Card
]
=
None
):
cards
:
List
[
Card
]
=
[]
if
cards
is
None
:
cards
=
[]
self
.
cards
:
List
[
Card
]
=
cards
def
__contains__
(
self
,
item
:
Card
):
def
__contains__
(
self
,
item
:
Card
):
return
item
in
self
.
cards
return
item
in
self
.
cards
...
...
server/model/hands.py
View file @
52760898
...
@@ -5,44 +5,52 @@ from server.model.card import Value, Color, Card
...
@@ -5,44 +5,52 @@ from server.model.card import Value, Color, Card
from
server.model.hand
import
Hand
from
server.model.hand
import
Hand
def
hand
(
cards
:
List
[
Card
])
->
Hand
:
return
Hand
(
cards
=
cards
)
def
card
(
value
:
Value
,
color
:
Color
)
->
Card
:
return
Card
(
value
=
value
,
color
=
color
)
def
carre
(
value
)
->
Hand
:
def
carre
(
value
)
->
Hand
:
return
H
and
([
return
h
and
([
C
ard
(
value
,
Color
.
Hearts
),
c
ard
(
value
,
Color
.
Hearts
),
C
ard
(
value
,
Color
.
Clubs
),
c
ard
(
value
,
Color
.
Clubs
),
C
ard
(
value
,
Color
.
Diamonds
),
c
ard
(
value
,
Color
.
Diamonds
),
C
ard
(
value
,
Color
.
Spades
)
c
ard
(
value
,
Color
.
Spades
)
])
])
def
full
(
value_aux
:
Value
,
def
full
(
value_aux
:
Value
,
value_par
:
Value
)
->
Hand
:
value_par
:
Value
)
->
Hand
:
return
H
and
([
return
h
and
([
C
ard
(
value_aux
,
Color
.
Hearts
),
c
ard
(
value_aux
,
Color
.
Hearts
),
C
ard
(
value_aux
,
Color
.
Clubs
),
c
ard
(
value_aux
,
Color
.
Clubs
),
C
ard
(
value_aux
,
Color
.
Diamonds
),
c
ard
(
value_aux
,
Color
.
Diamonds
),
C
ard
(
value_par
,
Color
.
Hearts
),
c
ard
(
value_par
,
Color
.
Hearts
),
C
ard
(
value_par
,
Color
.
Clubs
)
c
ard
(
value_par
,
Color
.
Clubs
)
])
])
def
brelan
(
value
)
->
Hand
:
def
brelan
(
value
)
->
Hand
:
return
H
and
([
return
h
and
([
C
ard
(
value
,
Color
.
Hearts
),
c
ard
(
value
,
Color
.
Hearts
),
C
ard
(
value
,
Color
.
Clubs
),
c
ard
(
value
,
Color
.
Clubs
),
C
ard
(
value
,
Color
.
Diamonds
)
c
ard
(
value
,
Color
.
Diamonds
)
])
])
def
pair
(
value
)
->
Hand
:
def
pair
(
value
)
->
Hand
:
return
H
and
([
return
h
and
([
C
ard
(
value
,
Color
.
Hearts
),
c
ard
(
value
,
Color
.
Hearts
),
C
ard
(
value
,
Color
.
Clubs
)
c
ard
(
value
,
Color
.
Clubs
)
])
])
def
single
(
low_value
)
->
Hand
:
def
single
(
low_value
)
->
Hand
:
return
H
and
([
return
h
and
([
C
ard
(
low_value
,
Color
.
Hearts
)
c
ard
(
low_value
,
Color
.
Hearts
)
])
])
...
@@ -52,13 +60,13 @@ def double_pair(value: Value, other: Value = None):
...
@@ -52,13 +60,13 @@ def double_pair(value: Value, other: Value = None):
other
=
Value
.
Two
if
value
==
Value
.
Three
else
Value
.
Three
other
=
Value
.
Two
if
value
==
Value
.
Three
else
Value
.
Three
assert
other
!=
value
assert
other
!=
value
return
H
and
([
return
h
and
([
# Pair of value
# Pair of value
C
ard
(
value
,
Color
.
Hearts
),
c
ard
(
value
,
Color
.
Hearts
),
C
ard
(
value
,
Color
.
Clubs
),
c
ard
(
value
,
Color
.
Clubs
),
# And pair of twos or threes
# And pair of twos or threes
C
ard
(
other
,
Color
.
Hearts
),
c
ard
(
other
,
Color
.
Hearts
),
C
ard
(
other
,
Color
.
Clubs
)
c
ard
(
other
,
Color
.
Clubs
)
])
])
...
@@ -77,7 +85,7 @@ def all_options() -> List[Hand]:
...
@@ -77,7 +85,7 @@ def all_options() -> List[Hand]:
for
b
in
brelans
:
for
b
in
brelans
:
for
p
in
pairs
:
for
p
in
pairs
:
if
not
any
([
c
in
b
.
cards
for
c
in
p
.
cards
]):
# Valid full
if
not
any
([
c
in
b
.
cards
for
c
in
p
.
cards
]):
# Valid full
hands
.
append
(
H
and
([
*
b
.
cards
,
*
p
.
cards
]))
hands
.
append
(
h
and
([
*
b
.
cards
,
*
p
.
cards
]))
return
hands
return
hands
...
...
server/model/known.py
View file @
52760898
from
server.model.card
import
Value
,
Color
,
Card
from
server.model.card
import
Value
,
Color
from
server.model.hands
import
pair
,
single
,
double_pair
,
brelan
,
full
,
carre
from
server.model.hands
import
pair
,
single
,
double_pair
,
brelan
,
full
,
carre
,
card
ACE_OF_HEARTS
=
C
ard
(
Value
.
Ace
,
Color
.
Hearts
)
ACE_OF_HEARTS
=
c
ard
(
Value
.
Ace
,
Color
.
Hearts
)
ACE_OF_SPADES
=
C
ard
(
Value
.
Ace
,
Color
.
Spades
)
ACE_OF_SPADES
=
c
ard
(
Value
.
Ace
,
Color
.
Spades
)
ACE_OF_DIAMONDS
=
C
ard
(
Value
.
Ace
,
Color
.
Diamonds
)
ACE_OF_DIAMONDS
=
c
ard
(
Value
.
Ace
,
Color
.
Diamonds
)
ACE_OF_CLUBS
=
C
ard
(
Value
.
Ace
,
Color
.
Clubs
)
ACE_OF_CLUBS
=
c
ard
(
Value
.
Ace
,
Color
.
Clubs
)
PAIR_ACE
=
pair
(
Value
.
Ace
)
PAIR_ACE
=
pair
(
Value
.
Ace
)
SINGLE_ACE
=
single
(
Value
.
Ace
)
SINGLE_ACE
=
single
(
Value
.
Ace
)
DOUBLE_PAIR_ACE
=
double_pair
(
Value
.
Ace
)
DOUBLE_PAIR_ACE
=
double_pair
(
Value
.
Ace
)
...
...
server/model/players.py
View file @
52760898
...
@@ -2,14 +2,15 @@ from abc import abstractmethod, ABC
...
@@ -2,14 +2,15 @@ from abc import abstractmethod, ABC
from
dataclasses
import
dataclass
from
dataclasses
import
dataclass
from
typing
import
Optional
from
typing
import
Optional
from
pydantic.main
import
BaseModel
from
server.model.animals
import
random_animal_name
from
server.model.animals
import
random_animal_name
from
server.model.card
import
Card
from
server.model.card
import
Card
from
server.model.hand
import
Hand
from
server.model.hand
import
Hand
from
server.model.hands
import
random_option
from
server.model.hands
import
random_option
@dataclass
class
Announce
(
BaseModel
):
class
Announce
:
bet
:
Optional
[
Hand
]
=
None
bet
:
Optional
[
Hand
]
=
None
@property
@property
...
@@ -55,7 +56,7 @@ class NaivePlayer(Player, ABC):
...
@@ -55,7 +56,7 @@ class NaivePlayer(Player, ABC):
def
announce
(
self
,
current_bet
:
Optional
[
Hand
])
->
Announce
:
def
announce
(
self
,
current_bet
:
Optional
[
Hand
])
->
Announce
:
if
not
current_bet
or
self
.
hand
.
value
()
>
current_bet
.
value
():
if
not
current_bet
or
self
.
hand
.
value
()
>
current_bet
.
value
():
return
Announce
(
self
.
hand
)
return
Announce
(
bet
=
self
.
hand
)
else
:
else
:
return
Announce
()
return
Announce
()
...
@@ -70,7 +71,7 @@ class RandomPlayer(Player, ABC):
...
@@ -70,7 +71,7 @@ class RandomPlayer(Player, ABC):
if
current_bet
and
not
current_bet
.
is_menteur
and
current_bet
.
is_carre_as
:
if
current_bet
and
not
current_bet
.
is_menteur
and
current_bet
.
is_carre_as
:
return
Announce
()
return
Announce
()
else
:
else
:
return
Announce
(
random_option
())
return
Announce
(
bet
=
random_option
())
class
MenteurPlayer
(
Player
,
ABC
):
class
MenteurPlayer
(
Player
,
ABC
):
...
@@ -81,4 +82,4 @@ class MenteurPlayer(Player, ABC):
...
@@ -81,4 +82,4 @@ class MenteurPlayer(Player, ABC):
def
announce
(
self
,
current_bet
:
Optional
[
Hand
])
->
Announce
:
def
announce
(
self
,
current_bet
:
Optional
[
Hand
])
->
Announce
:
hand
=
random_option
()
hand
=
random_option
()
return
Announce
(
hand
)
return
Announce
(
bet
=
hand
)
server/test/test_lobby.py
View file @
52760898
...
@@ -29,10 +29,7 @@ class MockPlayer(Player):
...
@@ -29,10 +29,7 @@ class MockPlayer(Player):
return
len
([
m
for
(
m
,
e
)
in
self
.
messages
if
m
is
msg
])
return
len
([
m
for
(
m
,
e
)
in
self
.
messages
if
m
is
msg
])
def
announce
(
self
,
current_bet
:
Optional
[
Hand
])
->
Announce
:
def
announce
(
self
,
current_bet
:
Optional
[
Hand
])
->
Announce
:
if
self
.
bets
:
return
Announce
(
bet
=
self
.
bets
.
pop
()
if
self
.
bets
else
CARRE_ACE
)
return
Announce
(
self
.
bets
.
pop
())
else
:
return
Announce
(
CARRE_ACE
)
def
receive
(
self
,
message
:
MessageToPlayer
,
extra
:
Optional
[
Any
]
=
None
):
def
receive
(
self
,
message
:
MessageToPlayer
,
extra
:
Optional
[
Any
]
=
None
):
self
.
messages
.
append
((
message
,
extra
))
self
.
messages
.
append
((
message
,
extra
))
...
@@ -92,8 +89,8 @@ class TestManager(TestCase):
...
@@ -92,8 +89,8 @@ class TestManager(TestCase):
self
.
assertIn
(
MessageToPlayer
.
Win
,
self
.
j1
.
messages
,
"Win not told"
)
self
.
assertIn
(
MessageToPlayer
.
Win
,
self
.
j1
.
messages
,
"Win not told"
)
self
.
assertIn
(
MessageToPlayer
.
Lose
,
self
.
j2
.
messages
,
"Lose not told"
)
self
.
assertIn
(
MessageToPlayer
.
Lose
,
self
.
j2
.
messages
,
"Lose not told"
)
def
assertGot
(
self
,
player
:
MockPlayer
,
typ
e
:
MessageToPlayer
,
extra
=
None
,
msg
:
str
=
"message not received"
):
def
assertGot
(
self
,
player
:
MockPlayer
,
messag
e
:
MessageToPlayer
,
extra
=
None
,
msg
:
str
=
"message not received"
):
self
.
assertIn
(
typ
e
,
[
t
for
(
t
,
_
)
in
player
.
messages
],
msg
)
self
.
assertIn
(
messag
e
,
[
t
for
(
t
,
_
)
in
player
.
messages
],
msg
)
if
extra
:
if
extra
:
matching
=
[
m
for
(
m
,
e
)
in
player
.
messages
if
e
==
extra
and
m
==
typ
e
]
matching
=
[
m
for
(
m
,
e
)
in
player
.
messages
if
e
==
extra
and
m
==
messag
e
]
self
.
assertTrue
(
matching
,
f
"No message {
typ
e} with extra {extra}"
)
self
.
assertTrue
(
matching
,
f
"No message {
messag
e} with extra {extra}"
)
server/ws.py
View file @
52760898
...
@@ -18,6 +18,7 @@ class ClientPlayer(Player):
...
@@ -18,6 +18,7 @@ class ClientPlayer(Player):
def
__init__
(
self
,
lobby
:
LobbyManager
):
def
__init__
(
self
,
lobby
:
LobbyManager
):
super
()
.
__init__
()
super
()
.
__init__
()
self
.
lobby
=
lobby
self
.
lobby
=
lobby
self
.
ready
=
False
async
def
announce
(
self
,
current_bet
:
Optional
[
Hand
])
->
Announce
:
async
def
announce
(
self
,
current_bet
:
Optional
[
Hand
])
->
Announce
:
return
lobby
.
last_announces
[
self
]
return
lobby
.
last_announces
[
self
]
...
@@ -27,13 +28,14 @@ class ClientPlayer(Player):
...
@@ -27,13 +28,14 @@ class ClientPlayer(Player):
async
def
connect
(
sid
,
environ
):
async
def
connect
(
sid
,
environ
):
print
(
"[WS] Connect "
,
sid
,
environ
)
print
(
"[WS] Connect "
,
sid
,
environ
)
player
=
ClientPlayer
(
lobby
)
player
=
ClientPlayer
(
lobby
)
await
lobby
.
add_player
(
player
)
reply
:
str
=
await
lobby
.
add_player
(
player
)
await
sio
.
emit
(
'messageChannel'
,
reply
,
room
=
sid
)
@sio.event
@sio.event
async
def
message
(
sid
,
data
):
async
def
message
(
sid
,
data
):
print
(
"[WS] Message "
,
data
)
print
(
"[WS] Message "
,
data
)
await
sio
.
emit
(
'messageChannel'
,
"OK"
,
room
=
sid
)
await
sio
.
emit
(
'messageChannel'
,
lobby
.
handle_message
(
sid
,
data
)
,
room
=
sid
)
@sio.on
(
"pingServer"
)
@sio.on
(
"pingServer"
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment