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
2967f490
Unverified
Commit
2967f490
authored
Apr 11, 2020
by
PLN (Algolia)
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(messages): Manager messages to player
parent
b8cdc814
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
160 additions
and
30 deletions
+160
-30
app.py
server/app.py
+0
-15
lobby.py
server/game/lobby.py
+2
-1
manager.py
server/game/manager.py
+5
-1
message.py
server/game/message.py
+4
-0
deck.py
server/model/deck.py
+7
-2
game.py
server/model/game.py
+45
-8
hands.py
server/model/hands.py
+1
-1
requirements.txt
server/requirements.txt
+1
-0
test_game.py
server/test/test_game.py
+2
-2
test_lobby.py
server/test/test_lobby.py
+93
-0
No files found.
server/app.py
View file @
2967f490
from
http.client
import
HTTPException
import
socketio
import
socketio
from
fastapi
import
FastAPI
,
APIRouter
from
fastapi
import
FastAPI
,
APIRouter
from
fastapi.exceptions
import
RequestValidationError
from
fastapi.exceptions
import
RequestValidationError
from
starlette.exceptions
import
HTTPException
from
starlette.exceptions
import
HTTPException
from
starlette.responses
import
PlainTextResponse
from
starlette.responses
import
PlainTextResponse
from
server.game.lobby
import
LobbyManager
from
server.model.data
import
Game
# Game state
# Game state
from
server.model.players
import
RandomPlayer
from
server.ws
import
sio
from
server.ws
import
sio
# Server
# Server
app
=
FastAPI
()
app
=
FastAPI
()
router
=
APIRouter
()
router
=
APIRouter
()
...
@@ -39,15 +33,6 @@ async def hello_world():
...
@@ -39,15 +33,6 @@ async def hello_world():
return
"Hello, gentle[wo]man"
return
"Hello, gentle[wo]man"
@router.post
(
"/join"
)
async
def
join
(
player_name
:
str
):
if
game
.
add_player
(
RandomPlayer
(
player_name
)
# TODO: Let user play
):
return
"Welcome
%
s,
%
i players currently waiting!"
%
(
player_name
,
len
(
game
.
players
))
else
:
raise
HTTPException
(
status_code
=
403
,
detail
=
f
"{player_name} already connected, choose another name."
)
app
.
include_router
(
router
)
app
.
include_router
(
router
)
...
...
server/game/lobby.py
View file @
2967f490
...
@@ -5,7 +5,7 @@ from socketio import AsyncServer
...
@@ -5,7 +5,7 @@ from socketio import AsyncServer
from
server.game.manager
import
ClientManager
from
server.game.manager
import
ClientManager
from
server.game.message
import
MessageToPlayer
from
server.game.message
import
MessageToPlayer
from
server.model.
data
import
Game
from
server.model.
game
import
Game
from
server.model.players
import
Player
,
Announce
from
server.model.players
import
Player
,
Announce
...
@@ -38,6 +38,7 @@ class LobbyManager(ClientManager):
...
@@ -38,6 +38,7 @@ class LobbyManager(ClientManager):
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"
self
.
last_announces
[
player
]
=
announce
self
.
last_announces
[
player
]
=
announce
print
(
f
"{player} ready to play! ({self.nb_ready} people ready)"
)
print
(
f
"{player} ready to play! ({self.nb_ready} people ready)"
)
...
...
server/game/manager.py
View file @
2967f490
...
@@ -9,6 +9,7 @@ class ClientManager(ABC):
...
@@ -9,6 +9,7 @@ class ClientManager(ABC):
"""
"""
A listener of game state that notifies clients.
A listener of game state that notifies clients.
"""
"""
def
__init__
(
self
):
def
__init__
(
self
):
self
.
players
:
List
[
Player
]
=
[]
self
.
players
:
List
[
Player
]
=
[]
...
@@ -18,5 +19,8 @@ class ClientManager(ABC):
...
@@ -18,5 +19,8 @@ class ClientManager(ABC):
self
.
send
(
p
,
message
)
self
.
send
(
p
,
message
)
@abstractmethod
@abstractmethod
def
send
(
self
,
to
:
Player
,
message
:
MessageToPlayer
,
extra
=
None
):
def
send
(
self
,
to
:
Player
,
message
:
MessageToPlayer
,
extra
=
None
):
raise
NotImplementedError
(
"Send a message to clients "
)
raise
NotImplementedError
(
"Send a message to clients "
)
server/game/message.py
View file @
2967f490
...
@@ -4,9 +4,13 @@ from enum import Enum
...
@@ -4,9 +4,13 @@ from enum import Enum
class
MessageToPlayer
(
Enum
):
class
MessageToPlayer
(
Enum
):
Waiting
=
"WAITING_ROOM"
Waiting
=
"WAITING_ROOM"
Ready
=
"READY_ROOM"
Ready
=
"READY_ROOM"
GiveHand
=
"GIVE_HAND"
WaitTurn
=
"WAITING_TURN"
WaitTurn
=
"WAITING_TURN"
YourTurn
=
"YOUR_TURN"
YourTurn
=
"YOUR_TURN"
Announce
=
"ANNOUNCE"
LoseRound
=
"LOSE_ROUND"
Win
=
"WINNER"
Win
=
"WINNER"
WinnerIs
=
"WINNER_IS"
Lose
=
"LOSER"
Lose
=
"LOSER"
...
...
server/model/deck.py
View file @
2967f490
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
class
Deck
:
class
Deck
:
def
__init__
(
self
):
def
__init__
(
self
,
cards
=
None
):
self
.
cards
=
[
Card
(
v
,
c
)
for
v
in
Value
for
c
in
Color
]
if
cards
is
None
:
cards
=
[
Card
(
v
,
c
)
for
v
in
Value
for
c
in
Color
]
else
:
print
(
"Deck init with cards:"
,
cards
,
len
(
cards
))
self
.
cards
=
cards
self
.
defausse
=
[]
self
.
defausse
=
[]
def
__len__
(
self
):
def
__len__
(
self
):
...
...
server/model/
data
.py
→
server/model/
game
.py
View file @
2967f490
...
@@ -3,10 +3,11 @@ from collections import defaultdict
...
@@ -3,10 +3,11 @@ from collections import defaultdict
from
typing
import
List
,
Dict
,
Optional
from
typing
import
List
,
Dict
,
Optional
from
server.game.manager
import
ClientManager
from
server.game.manager
import
ClientManager
from
server.model.card
import
Card
from
server.game.message
import
MessageToPlayer
from
server.model.card
import
Card
,
Value
from
server.model.deck
import
Deck
from
server.model.deck
import
Deck
from
server.model.hand
import
Hand
from
server.model.hand
import
Hand
from
server.model.players
import
Player
from
server.model.players
import
Player
,
Announce
class
Game
:
class
Game
:
...
@@ -22,6 +23,17 @@ class Game:
...
@@ -22,6 +23,17 @@ class Game:
self
.
players
:
List
[
Player
]
=
players
self
.
players
:
List
[
Player
]
=
players
self
.
defeats
:
Dict
[
Player
,
int
]
=
defaultdict
(
lambda
:
0
)
self
.
defeats
:
Dict
[
Player
,
int
]
=
defaultdict
(
lambda
:
0
)
self
.
current_bet
:
Optional
[
Hand
]
=
None
self
.
current_bet
:
Optional
[
Hand
]
=
None
self
.
manager
=
manager
def
message
(
self
,
message
:
MessageToPlayer
,
*
to
:
Player
,
extra
=
None
)
->
None
:
if
self
.
manager
:
if
not
to
:
to
=
self
.
players
for
player
in
to
:
self
.
manager
.
send
(
player
,
message
,
extra
)
@property
@property
def
global_hand
(
self
)
->
Hand
:
def
global_hand
(
self
)
->
Hand
:
...
@@ -35,13 +47,14 @@ class Game:
...
@@ -35,13 +47,14 @@ class Game:
if
self
.
defeats
[
loser
]
==
5
:
if
self
.
defeats
[
loser
]
==
5
:
print
(
f
"{loser} is eliminated!"
)
print
(
f
"{loser} is eliminated!"
)
self
.
message
(
MessageToPlayer
.
Lose
,
loser
)
self
.
players
.
remove
(
loser
)
self
.
players
.
remove
(
loser
)
else
:
else
:
print
(
f
"{loser} lost the round, now playing with {self.defeats[loser] + 1} cards!"
)
print
(
f
"{loser} lost the round, now playing with {self.defeats[loser] + 1} cards!"
)
self
.
players
.
remove
(
loser
)
self
.
players
.
insert
(
0
,
loser
)
winner
=
self
.
players
[
0
]
winner
=
self
.
players
[
0
]
self
.
message
(
MessageToPlayer
.
Win
,
winner
)
self
.
message
(
MessageToPlayer
.
WinnerIs
,
extra
=
winner
)
print
(
f
"Game over - {winner.name} wins with {len(winner.hand)} cards!"
)
print
(
f
"Game over - {winner.name} wins with {len(winner.hand)} cards!"
)
def
new_turn
(
self
)
->
Player
:
def
new_turn
(
self
)
->
Player
:
...
@@ -52,11 +65,19 @@ class Game:
...
@@ -52,11 +65,19 @@ class Game:
"""
"""
# Distribution
# Distribution
self
.
deck
.
reset
()
self
.
deck
.
reset
()
self
.
message
(
MessageToPlayer
.
WaitTurn
)
for
current_player
in
self
.
players
:
for
current_player
in
self
.
players
:
current_player
.
clear
()
current_player
.
clear
()
for
i
in
range
(
self
.
defeats
[
current_player
]
+
1
):
count_player_cards
=
self
.
defeats
[
current_player
]
+
1
current_player
.
give
(
self
.
deck
.
random_card
())
print
(
f
"Drawing {count_player_cards} card(s) for {current_player}: "
,
end
=
""
)
print
(
f
"Drew {current_player.hand} for {current_player}."
)
for
i
in
range
(
count_player_cards
):
card
=
self
.
deck
.
random_card
()
current_player
.
give
(
card
)
print
(
f
"{card}"
)
self
.
message
(
MessageToPlayer
.
GiveHand
,
current_player
,
extra
=
current_player
.
hand
)
print
(
f
"Cards sent."
)
# Tour
# Tour
self
.
current_bet
=
None
self
.
current_bet
=
None
...
@@ -64,6 +85,9 @@ class Game:
...
@@ -64,6 +85,9 @@ class Game:
for
current_player
in
itertools
.
cycle
(
self
.
players
):
for
current_player
in
itertools
.
cycle
(
self
.
players
):
loser
=
self
.
play_turn
(
current_player
,
last_player
)
loser
=
self
.
play_turn
(
current_player
,
last_player
)
if
loser
is
not
None
:
if
loser
is
not
None
:
self
.
players
.
remove
(
loser
)
self
.
players
.
insert
(
0
,
loser
)
self
.
message
(
MessageToPlayer
.
LoseRound
,
loser
)
return
loser
return
loser
last_player
=
current_player
last_player
=
current_player
...
@@ -117,14 +141,26 @@ class Game:
...
@@ -117,14 +141,26 @@ class Game:
print
(
f
"| {current_player}'s turn >"
)
print
(
f
"| {current_player}'s turn >"
)
if
not
self
.
current_bet
:
# First player, has to bet something
if
not
self
.
current_bet
:
# First player, has to bet something
self
.
message
(
MessageToPlayer
.
YourTurn
,
current_player
,
extra
=
self
.
current_bet
)
while
not
self
.
current_bet
:
# Ask a valid bet
while
not
self
.
current_bet
:
# Ask a valid bet
# FIXME: Wait for player announce? Maybe just sleep 10?
announce
=
current_player
.
announce
(
self
.
current_bet
)
announce
=
current_player
.
announce
(
self
.
current_bet
)
if
announce
.
bet
:
if
announce
.
bet
:
self
.
current_bet
=
announce
.
bet
self
.
current_bet
=
announce
.
bet
print
(
f
"{current_player} starts the round: {self.current_bet}"
)
print
(
f
"{current_player} starts the round: {self.current_bet}"
)
self
.
message
(
MessageToPlayer
.
Announce
,
extra
=
{
"player"
:
current_player
,
"announce"
:
announce
})
else
:
print
(
f
"You cannot say Menteur on first round, {current_player}!"
)
else
:
# Next player, announce or menteur
else
:
# Next player, announce or menteur
announce
=
current_player
.
announce
(
self
.
current_bet
)
# Wait, is the announce the last possible one?
if
len
(
self
.
current_bet
.
cards
)
==
4
and
all
([
c
.
value
is
Value
.
Ace
for
c
in
self
.
current_bet
.
cards
]):
print
(
"CARRE D'AS!"
)
announce
=
Announce
()
# MENTEUR obligatoire
else
:
self
.
message
(
MessageToPlayer
.
YourTurn
,
current_player
,
extra
=
self
.
current_bet
)
announce
=
current_player
.
announce
(
self
.
current_bet
)
if
announce
.
bet
:
if
announce
.
bet
:
while
announce
.
bet
.
value
()
<
self
.
current_bet
.
value
():
# Bad announce!
while
announce
.
bet
.
value
()
<
self
.
current_bet
.
value
():
# Bad announce!
...
@@ -134,6 +170,7 @@ class Game:
...
@@ -134,6 +170,7 @@ class Game:
# Valid bet:
# Valid bet:
print
(
f
" {current_player} bets {self.current_bet}."
)
print
(
f
" {current_player} bets {self.current_bet}."
)
self
.
message
(
MessageToPlayer
.
Announce
,
extra
=
{
"player"
:
current_player
,
"announce"
:
announce
})
else
:
# Menteur! Who lost the round?
else
:
# Menteur! Who lost the round?
menteur
=
self
.
is_menteur
(
self
.
current_bet
)
menteur
=
self
.
is_menteur
(
self
.
current_bet
)
...
...
server/model/hands.py
View file @
2967f490
...
@@ -19,7 +19,7 @@ def full(value_aux: Value,
...
@@ -19,7 +19,7 @@ def full(value_aux: Value,
return
Hand
([
return
Hand
([
Card
(
value_aux
,
Color
.
Hearts
),
Card
(
value_aux
,
Color
.
Hearts
),
Card
(
value_aux
,
Color
.
Clubs
),
Card
(
value_aux
,
Color
.
Clubs
),
Card
(
value_aux
,
Color
.
Spade
s
),
Card
(
value_aux
,
Color
.
Diamond
s
),
Card
(
value_par
,
Color
.
Hearts
),
Card
(
value_par
,
Color
.
Hearts
),
Card
(
value_par
,
Color
.
Clubs
)
Card
(
value_par
,
Color
.
Clubs
)
])
])
...
...
server/requirements.txt
View file @
2967f490
fastapi==0.53.2
fastapi==0.53.2
uvicorn==0.11.3
uvicorn==0.11.3
python-socketio==4.5.1
server/test/test_game.py
View file @
2967f490
from
unittest
import
TestCase
from
unittest
import
TestCase
from
server.model.
data
import
Game
from
server.model.
game
import
Game
from
server.model.players
import
MenteurPlayer
,
NaivePlayer
,
RandomPlayer
from
server.model.players
import
MenteurPlayer
,
NaivePlayer
,
RandomPlayer
...
@@ -38,8 +38,8 @@ class TestGame(TestCase):
...
@@ -38,8 +38,8 @@ class TestGame(TestCase):
self
.
assertEqual
(
1
,
len
([
v
for
v
in
self
.
game
.
defeats
.
values
()
if
v
>
0
]),
"There should have been one defeat."
)
self
.
assertEqual
(
1
,
len
([
v
for
v
in
self
.
game
.
defeats
.
values
()
if
v
>
0
]),
"There should have been one defeat."
)
self
.
assertTrue
(
1
in
self
.
game
.
defeats
.
values
(),
"A player should have one defeat."
)
self
.
assertTrue
(
1
in
self
.
game
.
defeats
.
values
(),
"A player should have one defeat."
)
loser
=
[
p
for
p
in
self
.
game
.
players
if
self
.
game
.
defeats
[
p
]][
0
]
loser
=
[
p
for
p
in
self
.
game
.
players
if
self
.
game
.
defeats
[
p
]][
0
]
self
.
assertEqual
(
self
.
game
.
players
[
0
],
loser
,
"The loser should be first to play."
)
self
.
assertEqual
(
self
.
game
.
players
[
0
],
loser
,
"The loser should be first to play."
)
def
test_full_game
(
self
):
def
test_full_game
(
self
):
...
...
server/test/test_lobby.py
0 → 100644
View file @
2967f490
from
typing
import
Optional
,
List
from
unittest
import
TestCase
from
server.game.manager
import
ClientManager
from
server.game.message
import
MessageToPlayer
from
server.model.card
import
Value
from
server.model.deck
import
Deck
from
server.model.game
import
Game
from
server.model.hand
import
Hand
from
server.model.hands
import
pair
,
brelan
from
server.model.known
import
CARRE_ACE
from
server.model.players
import
Announce
,
Player
class
MockPlayer
(
Player
):
def
__init__
(
self
,
name
:
str
=
None
,
bets
:
List
[
Optional
[
Hand
]]
=
None
):
super
()
.
__init__
(
name
)
if
bets
is
None
:
bets
=
[]
self
.
bets
:
List
[
Optional
[
Hand
]]
=
bets
self
.
messages
:
List
[
MessageToPlayer
]
=
[]
def
count
(
self
,
msg
:
MessageToPlayer
)
->
int
:
return
len
([
m
for
m
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
)
def
receive
(
self
,
message
:
MessageToPlayer
):
self
.
messages
.
append
(
message
)
def
print_msgs
(
self
)
->
str
:
'|'
.
join
([
str
(
m
)
for
m
in
self
.
messages
])
class
MockManager
(
ClientManager
):
def
__init__
(
self
,
players
:
List
[
MockPlayer
]):
super
()
.
__init__
()
self
.
players
=
players
def
send
(
self
,
to
:
Player
,
message
:
MessageToPlayer
,
extra
=
None
):
if
isinstance
(
to
,
MockPlayer
):
to
.
receive
(
message
)
print
(
f
"Sent {message} to {to}."
)
class
TestManager
(
TestCase
):
def
setUp
(
self
)
->
None
:
self
.
j1
=
MockPlayer
(
"j1"
,
[
pair
(
Value
.
Ace
)])
self
.
j2
=
MockPlayer
(
"j2"
,
[
brelan
(
Value
.
Two
)])
self
.
manager
=
MockManager
([
self
.
j1
,
self
.
j2
])
cards
=
[]
cards
.
extend
(
self
.
j1
.
bets
[
0
]
.
cards
)
cards
.
extend
(
self
.
j2
.
bets
[
0
]
.
cards
)
self
.
game
=
Game
(
players
=
[
self
.
j1
,
self
.
j2
],
deck
=
Deck
(
cards
=
cards
),
manager
=
self
.
manager
)
def
test_turn_messages
(
self
):
self
.
game
.
new_turn
()
self
.
assertEqual
(
self
.
j1
.
count
(
MessageToPlayer
.
LoseRound
),
6
,
f
"j1 should lose 6 rounds."
)
self
.
assertEqual
(
self
.
j2
.
count
(
MessageToPlayer
.
LoseRound
),
6
,
f
"j2 should lose 6 rounds."
)
for
player
in
[
self
.
j1
,
self
.
j2
]:
self
.
assertIn
(
MessageToPlayer
.
Lose
,
player
.
messages
,
"Loser not announced"
)
def
test_game_messages
(
self
):
self
.
game
.
new_game
()
self
.
assertEqual
(
len
([
m
for
m
in
self
.
j1
.
messages
if
m
is
MessageToPlayer
.
LoseRound
]),
5
,
f
"{self.j1} should lose 5 rounds: {'|'.join([str(m) for m in self.j1.messages])}"
)
self
.
assertEqual
(
self
.
j1
.
count
(
MessageToPlayer
.
YourTurn
),
6
,
f
"{self.j1} should play 6 rounds: {self.j1.print_msgs()}"
)
self
.
assertEqual
(
self
.
j2
.
count
(
MessageToPlayer
.
YourTurn
),
1
,
f
"{self.j2} should play 1 rounds: {self.j2.print_msgs()}"
)
self
.
assertEqual
(
len
([
m
for
m
in
self
.
j2
.
messages
if
m
is
MessageToPlayer
.
LoseRound
]),
0
,
f
"{self.j2} should lose 0 rounds: {'|'.join([str(m) for m in self.j2.messages])}"
)
for
player
in
[
self
.
j1
,
self
.
j2
]:
self
.
assertEqual
(
player
.
count
(
MessageToPlayer
.
Announce
),
7
,
f
"{player} should see 7 announces: {player.print_msgs()}"
)
self
.
assertIn
(
MessageToPlayer
.
WinnerIs
,
player
.
messages
,
"Winner not announced"
)
self
.
assertIn
(
MessageToPlayer
.
Win
,
player
.
messages
,
"Win not told"
)
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