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
4 years ago
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
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
)
This diff is collapsed.
Click to expand it.
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
...
...
@@ -57,4 +57,4 @@ def lowest_value_and_rest():
otherValues
=
list
(
Value
)
otherValues
.
remove
(
lowValue
)
return
lowValue
,
otherValues
\ No newline at end of file
return
lowValue
,
otherValues
This diff is collapsed.
Click to expand it.
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
...
...
This diff is collapsed.
Click to expand it.
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
...
...
This diff is collapsed.
Click to expand it.
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
...
...
This diff is collapsed.
Click to expand it.
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
)
...
...
This diff is collapsed.
Click to expand it.
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
)
This diff is collapsed.
Click to expand it.
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}"
)
This diff is collapsed.
Click to expand it.
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"
)
...
...
This diff is collapsed.
Click to expand it.
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