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
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
101 additions
and
67 deletions
+101
-67
lobby.py
server/game/lobby.py
+38
-10
card.py
server/model/card.py
+3
-3
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
from
typing
import
List
,
Dict
import
json
from
typing
import
List
,
Dict
,
Optional
from
pydantic.main
import
BaseModel
from
socketio
import
AsyncServer
from
server.game.manager
import
ClientManager
...
...
@@ -9,14 +10,20 @@ from server.model.game import Game
from
server.model.players
import
Player
,
Announce
class
Metadata
(
BaseModel
):
ready
:
bool
=
False
sid
:
str
=
""
last_announce
:
Optional
[
Announce
]
=
None
class
LobbyManager
(
ClientManager
):
""" A ClientManager that handles a lobby, then orchestrates games.
"""
def
__init__
(
self
,
sio
:
AsyncServer
):
super
()
.
__init__
()
self
.
l
ast_announces
:
Dict
[
Player
,
Announce
]
=
{}
self
.
lobby
:
Dict
[
Player
,
bool
]
=
{}
self
.
l
obby
:
Dict
[
str
,
Player
]
=
{}
self
.
metadata
:
Dict
[
str
,
Metadata
]
=
{}
self
.
games
:
List
[
Game
]
=
[]
self
.
sio
=
sio
...
...
@@ -26,28 +33,49 @@ class LobbyManager(ClientManager):
@property
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
:
self
.
lobby
[
player
]
=
is_ready
async
def
add_player
(
self
,
player
:
Player
,
is_ready
:
bool
=
False
)
->
str
:
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."
)
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
):
self
.
lobby
[
player
]
=
True
self
.
metadata
[
player
.
name
]
.
ready
=
True
print
(
f
"{player} ready to play! ({self.nb_ready} people ready)"
)
def
announces
(
self
,
player
:
Player
,
announce
:
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)"
)
def
start_game
(
self
,
max_players
=
2
):
players
=
self
.
players_ready
[:
max_players
]
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])}"
)
def
send
(
self
,
to
:
Player
,
message
:
MessageToPlayer
,
extra
=
None
):
self
.
sio
.
send
(
message
)
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
typing
import
Optional
from
pydantic.main
import
BaseModel
class
Value
(
Enum
):
Two
=
1
...
...
@@ -26,8 +27,7 @@ class Color(Enum):
Diamonds
=
"♦"
@dataclass
(
frozen
=
True
)
class
Card
:
class
Card
(
BaseModel
):
value
:
Value
color
:
Optional
[
Color
]
=
None
...
...
server/model/deck.py
View file @
52760898
from
random
import
randrange
,
shuffle
from
typing
import
List
from
server.model.card
import
Card
,
Value
,
Color
...
...
@@ -7,7 +6,7 @@ from server.model.card import Card, Value, Color
class
Deck
:
def
__init__
(
self
,
cards
=
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
:
print
(
"Deck init with cards:"
,
cards
,
len
(
cards
))
self
.
cards
=
cards
...
...
server/model/hand.py
View file @
52760898
from
collections
import
Counter
from
typing
import
List
from
pydantic.main
import
BaseModel
from
server.model.card
import
Card
,
Value
class
Hand
:
def
__init__
(
self
,
cards
:
List
[
Card
]
=
None
):
if
cards
is
None
:
cards
=
[]
self
.
cards
:
List
[
Card
]
=
cards
class
Hand
(
BaseModel
):
cards
:
List
[
Card
]
=
[]
def
__contains__
(
self
,
item
:
Card
):
return
item
in
self
.
cards
...
...
server/model/hands.py
View file @
52760898
...
...
@@ -5,44 +5,52 @@ from server.model.card import Value, Color, Card
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
:
return
H
and
([
C
ard
(
value
,
Color
.
Hearts
),
C
ard
(
value
,
Color
.
Clubs
),
C
ard
(
value
,
Color
.
Diamonds
),
C
ard
(
value
,
Color
.
Spades
)
return
h
and
([
c
ard
(
value
,
Color
.
Hearts
),
c
ard
(
value
,
Color
.
Clubs
),
c
ard
(
value
,
Color
.
Diamonds
),
c
ard
(
value
,
Color
.
Spades
)
])
def
full
(
value_aux
:
Value
,
value_par
:
Value
)
->
Hand
:
return
H
and
([
C
ard
(
value_aux
,
Color
.
Hearts
),
C
ard
(
value_aux
,
Color
.
Clubs
),
C
ard
(
value_aux
,
Color
.
Diamonds
),
C
ard
(
value_par
,
Color
.
Hearts
),
C
ard
(
value_par
,
Color
.
Clubs
)
return
h
and
([
c
ard
(
value_aux
,
Color
.
Hearts
),
c
ard
(
value_aux
,
Color
.
Clubs
),
c
ard
(
value_aux
,
Color
.
Diamonds
),
c
ard
(
value_par
,
Color
.
Hearts
),
c
ard
(
value_par
,
Color
.
Clubs
)
])
def
brelan
(
value
)
->
Hand
:
return
H
and
([
C
ard
(
value
,
Color
.
Hearts
),
C
ard
(
value
,
Color
.
Clubs
),
C
ard
(
value
,
Color
.
Diamonds
)
return
h
and
([
c
ard
(
value
,
Color
.
Hearts
),
c
ard
(
value
,
Color
.
Clubs
),
c
ard
(
value
,
Color
.
Diamonds
)
])
def
pair
(
value
)
->
Hand
:
return
H
and
([
C
ard
(
value
,
Color
.
Hearts
),
C
ard
(
value
,
Color
.
Clubs
)
return
h
and
([
c
ard
(
value
,
Color
.
Hearts
),
c
ard
(
value
,
Color
.
Clubs
)
])
def
single
(
low_value
)
->
Hand
:
return
H
and
([
C
ard
(
low_value
,
Color
.
Hearts
)
return
h
and
([
c
ard
(
low_value
,
Color
.
Hearts
)
])
...
...
@@ -52,13 +60,13 @@ def double_pair(value: Value, other: Value = None):
other
=
Value
.
Two
if
value
==
Value
.
Three
else
Value
.
Three
assert
other
!=
value
return
H
and
([
return
h
and
([
# Pair of value
C
ard
(
value
,
Color
.
Hearts
),
C
ard
(
value
,
Color
.
Clubs
),
c
ard
(
value
,
Color
.
Hearts
),
c
ard
(
value
,
Color
.
Clubs
),
# And pair of twos or threes
C
ard
(
other
,
Color
.
Hearts
),
C
ard
(
other
,
Color
.
Clubs
)
c
ard
(
other
,
Color
.
Hearts
),
c
ard
(
other
,
Color
.
Clubs
)
])
...
...
@@ -77,7 +85,7 @@ def all_options() -> List[Hand]:
for
b
in
brelans
:
for
p
in
pairs
:
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
...
...
server/model/known.py
View file @
52760898
from
server.model.card
import
Value
,
Color
,
Card
from
server.model.hands
import
pair
,
single
,
double_pair
,
brelan
,
full
,
carre
from
server.model.card
import
Value
,
Color
from
server.model.hands
import
pair
,
single
,
double_pair
,
brelan
,
full
,
carre
,
card
ACE_OF_HEARTS
=
C
ard
(
Value
.
Ace
,
Color
.
Hearts
)
ACE_OF_SPADES
=
C
ard
(
Value
.
Ace
,
Color
.
Spades
)
ACE_OF_DIAMONDS
=
C
ard
(
Value
.
Ace
,
Color
.
Diamonds
)
ACE_OF_CLUBS
=
C
ard
(
Value
.
Ace
,
Color
.
Clubs
)
ACE_OF_HEARTS
=
c
ard
(
Value
.
Ace
,
Color
.
Hearts
)
ACE_OF_SPADES
=
c
ard
(
Value
.
Ace
,
Color
.
Spades
)
ACE_OF_DIAMONDS
=
c
ard
(
Value
.
Ace
,
Color
.
Diamonds
)
ACE_OF_CLUBS
=
c
ard
(
Value
.
Ace
,
Color
.
Clubs
)
PAIR_ACE
=
pair
(
Value
.
Ace
)
SINGLE_ACE
=
single
(
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
from
dataclasses
import
dataclass
from
typing
import
Optional
from
pydantic.main
import
BaseModel
from
server.model.animals
import
random_animal_name
from
server.model.card
import
Card
from
server.model.hand
import
Hand
from
server.model.hands
import
random_option
@dataclass
class
Announce
:
class
Announce
(
BaseModel
):
bet
:
Optional
[
Hand
]
=
None
@property
...
...
@@ -55,7 +56,7 @@ class NaivePlayer(Player, ABC):
def
announce
(
self
,
current_bet
:
Optional
[
Hand
])
->
Announce
:
if
not
current_bet
or
self
.
hand
.
value
()
>
current_bet
.
value
():
return
Announce
(
self
.
hand
)
return
Announce
(
bet
=
self
.
hand
)
else
:
return
Announce
()
...
...
@@ -70,7 +71,7 @@ class RandomPlayer(Player, ABC):
if
current_bet
and
not
current_bet
.
is_menteur
and
current_bet
.
is_carre_as
:
return
Announce
()
else
:
return
Announce
(
random_option
())
return
Announce
(
bet
=
random_option
())
class
MenteurPlayer
(
Player
,
ABC
):
...
...
@@ -81,4 +82,4 @@ class MenteurPlayer(Player, ABC):
def
announce
(
self
,
current_bet
:
Optional
[
Hand
])
->
Announce
:
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):
return
len
([
m
for
(
m
,
e
)
in
self
.
messages
if
m
is
msg
])
def
announce
(
self
,
current_bet
:
Optional
[
Hand
])
->
Announce
:
if
self
.
bets
:
return
Announce
(
self
.
bets
.
pop
())
else
:
return
Announce
(
CARRE_ACE
)
return
Announce
(
bet
=
self
.
bets
.
pop
()
if
self
.
bets
else
CARRE_ACE
)
def
receive
(
self
,
message
:
MessageToPlayer
,
extra
:
Optional
[
Any
]
=
None
):
self
.
messages
.
append
((
message
,
extra
))
...
...
@@ -92,8 +89,8 @@ class TestManager(TestCase):
self
.
assertIn
(
MessageToPlayer
.
Win
,
self
.
j1
.
messages
,
"Win 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"
):
self
.
assertIn
(
typ
e
,
[
t
for
(
t
,
_
)
in
player
.
messages
],
msg
)
def
assertGot
(
self
,
player
:
MockPlayer
,
messag
e
:
MessageToPlayer
,
extra
=
None
,
msg
:
str
=
"message not received"
):
self
.
assertIn
(
messag
e
,
[
t
for
(
t
,
_
)
in
player
.
messages
],
msg
)
if
extra
:
matching
=
[
m
for
(
m
,
e
)
in
player
.
messages
if
e
==
extra
and
m
==
typ
e
]
self
.
assertTrue
(
matching
,
f
"No message {
typ
e} with extra {extra}"
)
matching
=
[
m
for
(
m
,
e
)
in
player
.
messages
if
e
==
extra
and
m
==
messag
e
]
self
.
assertTrue
(
matching
,
f
"No message {
messag
e} with extra {extra}"
)
server/ws.py
View file @
52760898
...
...
@@ -18,6 +18,7 @@ class ClientPlayer(Player):
def
__init__
(
self
,
lobby
:
LobbyManager
):
super
()
.
__init__
()
self
.
lobby
=
lobby
self
.
ready
=
False
async
def
announce
(
self
,
current_bet
:
Optional
[
Hand
])
->
Announce
:
return
lobby
.
last_announces
[
self
]
...
...
@@ -27,13 +28,14 @@ class ClientPlayer(Player):
async
def
connect
(
sid
,
environ
):
print
(
"[WS] Connect "
,
sid
,
environ
)
player
=
ClientPlayer
(
lobby
)
await
lobby
.
add_player
(
player
)
reply
:
str
=
await
lobby
.
add_player
(
player
)
await
sio
.
emit
(
'messageChannel'
,
reply
,
room
=
sid
)
@sio.event
async
def
message
(
sid
,
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"
)
...
...
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