updated roles, init tests, added states
This commit is contained in:
50
README.md
50
README.md
@@ -1,3 +1,53 @@
|
|||||||
# PyWerwolf
|
# PyWerwolf
|
||||||
|
|
||||||
Trying to rebuild [https://github.com/foin137/werwolfonline.eu](https://github.com/foin137/werwolfonline.eu) in python
|
Trying to rebuild [https://github.com/foin137/werwolfonline.eu](https://github.com/foin137/werwolfonline.eu) in python
|
||||||
|
|
||||||
|
|
||||||
|
## Phases of the game
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
[*] --> WaitingForPlayers: Created
|
||||||
|
WaitingForPlayers : Show the game id/link
|
||||||
|
WaitingForPlayers : Create game in Database
|
||||||
|
WaitingForPlayers : Allow setting of rules
|
||||||
|
|
||||||
|
WaitingForPlayers --> StartGame: len(Players)>=len(special roles) && start && num_werewolfes > 1
|
||||||
|
WaitingForPlayers --> WaitingForPlayers: start && (len(Players)<len(special roles) || num_werewolfes < 1)
|
||||||
|
StartGame : Assign Player Roles
|
||||||
|
StartGame : Show Introduction
|
||||||
|
|
||||||
|
state GameHasCupin <<choice>>
|
||||||
|
state NightPhaseCupin <<fork>>
|
||||||
|
StartGame --> GameHasCupin : all ready
|
||||||
|
GameHasCupin --> NightPhaseCupin: Game has Cupin
|
||||||
|
GameHasCupin --> NightPhaseMain: no Cupin
|
||||||
|
NightPhaseMain : Werewolfes awake & select (vote) their victim
|
||||||
|
NightPhaseMain : Seer/Spy/Protector/ParanormalInvestigator awaken & access feat
|
||||||
|
NightPhaseMain : sleep & confirm
|
||||||
|
|
||||||
|
|
||||||
|
state NightPhaseCupinEnd <<join>>
|
||||||
|
NightPhaseCupin --> WaitToContinueCupin: !Cupin
|
||||||
|
NightPhaseCupin --> SelectLovedOnes: Cupin
|
||||||
|
WaitToContinueCupin --> NightPhaseCupinEnd
|
||||||
|
SelectLovedOnes --> NightPhaseCupinEnd: selected and confirmed loved ones
|
||||||
|
|
||||||
|
state NightPhaseLovedOnes <<fork>>
|
||||||
|
state NightPhaseLovedOnesEnd <<fork>>
|
||||||
|
NightPhaseCupinEnd --> NightPhaseLovedOnes
|
||||||
|
NightPhaseLovedOnes --> LovedOnesAwake: Selected Loved Ones
|
||||||
|
NightPhaseLovedOnes --> WaitForLovedOnes: other
|
||||||
|
LovedOnesAwake --> NightPhaseLovedOnesEnd: ready
|
||||||
|
WaitForLovedOnes --> NightPhaseLovedOnesEnd
|
||||||
|
|
||||||
|
NightPhaseLovedOnesEnd --> NightPhaseMain : all ready
|
||||||
|
NightPhaseMain --> NightPhaseLate : Witch/Leaderwolf && voted && actions done and all ready
|
||||||
|
|
||||||
|
NightPhaseLate: LeaderWolf selects victim to convert
|
||||||
|
NightPhaseLate: witch is allowed to select heal/murder
|
||||||
|
|
||||||
|
NightPhaseMain --> ShowDead: no Witch/Leaderwolf && voted && actions done and all ready
|
||||||
|
NightPhaseLate --> ShowDead: actions taken
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
|||||||
@@ -1,3 +1,60 @@
|
|||||||
import models
|
from pywerwolf import models
|
||||||
|
import typing as t
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
class Game(object):
|
||||||
|
state: models.GameState
|
||||||
|
|
||||||
|
def __init__(self, game_id: t.Union[str, uuid.UUID] = None):
|
||||||
|
if game_id is None:
|
||||||
|
self.create_new_game_state()
|
||||||
|
else:
|
||||||
|
self.load_game_state(game_id)
|
||||||
|
|
||||||
|
def create_new_game_state(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def load_game_state(self, game_id: t.Union[str, uuid.UUID]):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def valid_game(self):
|
||||||
|
# check if game has been created or loaded
|
||||||
|
return True
|
||||||
|
|
||||||
|
def check_victory(self):
|
||||||
|
"""check for a winner of the current game"""
|
||||||
|
if not self.valid_game():
|
||||||
|
return None
|
||||||
|
|
||||||
|
if self.state.currentPhase == models.GamePhase.Award:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if sum(1 for _ in self.werewolfes) > 0:
|
||||||
|
if sum(1 for _ in self.villagers) <= 0:
|
||||||
|
return models.RoleGroup.Werewolfs
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return models.RoleGroup.Villagers
|
||||||
|
|
||||||
|
if sum(1 for _ in self.player_alive) == 2:
|
||||||
|
living = list(self.player_alive)
|
||||||
|
if living[0].lovedOne == living[1]:
|
||||||
|
return models.RoleGroup.LovedOnes
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def player_alive(self):
|
||||||
|
"""Generator for players still alive"""
|
||||||
|
return filter(lambda x: x.alive, self.state.players)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def werewolfes(self):
|
||||||
|
"""Generator for players that are werewolfes"""
|
||||||
|
return filter(models.Player.isWerwolf, self.player_alive)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def villagers(self):
|
||||||
|
"""Generator for players that are villagers"""
|
||||||
|
return filter(not (models.Player.isWerwolf), self.player_alive)
|
||||||
|
|||||||
@@ -21,6 +21,13 @@ class GamePhase(enum.Enum):
|
|||||||
PollResult = enum.auto()
|
PollResult = enum.auto()
|
||||||
Award = enum.auto()
|
Award = enum.auto()
|
||||||
|
|
||||||
|
class RoleGroup(enum.Enum):
|
||||||
|
NoGroup = enum.auto()
|
||||||
|
Villagers = enum.auto()
|
||||||
|
Werewolfs = enum.auto()
|
||||||
|
LovedOnes = enum.auto()
|
||||||
|
|
||||||
|
|
||||||
class Roles(enum.Enum):
|
class Roles(enum.Enum):
|
||||||
NoRole = enum.auto()
|
NoRole = enum.auto()
|
||||||
Villager = enum.auto()
|
Villager = enum.auto()
|
||||||
@@ -31,35 +38,52 @@ class Roles(enum.Enum):
|
|||||||
Cupid = enum.auto()
|
Cupid = enum.auto()
|
||||||
Protector = enum.auto()
|
Protector = enum.auto()
|
||||||
ParanormalInvestigator = enum.auto()
|
ParanormalInvestigator = enum.auto()
|
||||||
Lycantrop = enum.auto()
|
Lycantroph = enum.auto()
|
||||||
Spy = enum.auto()
|
Spy = enum.auto()
|
||||||
Murder = enum.auto()
|
|
||||||
Pacifist = enum.auto()
|
Pacifist = enum.auto()
|
||||||
OldMan = enum.auto()
|
OldMan = enum.auto()
|
||||||
Murder = enum.auto()
|
Murder = enum.auto()
|
||||||
Leaderwolf = enum.auto()
|
Leaderwolf = enum.auto()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def isWerwolf(cls, role: Roles):
|
||||||
|
return role in [cls.Werewolf, cls.Leaderwolf]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def getGroup(cls, role: Roles):
|
||||||
|
return RoleGroup.Werewolfs if role in [cls.Werewolf, cls.Leaderwolf] else RoleGroup.Villagers
|
||||||
|
|
||||||
class Rules(object):
|
class Rules(object):
|
||||||
showRoles: bool #`charaktereAufdecken` INT ( 2 ) DEFAULT 0,
|
showRoles: bool #`charaktereAufdecken` INT ( 2 ) DEFAULT 0,
|
||||||
passMajor: bool #`buergermeisterWeitergeben` INT ( 2 ) DEFAULT 0,
|
passMajor: bool #`buergermeisterWeitergeben` INT ( 2 ) DEFAULT 0,
|
||||||
seerSeesIdentity: bool #`seherSiehtIdentitaet` INT ( 2 ) DEFAULT 1,
|
seerSeesIdentity: bool #`seherSiehtIdentitaet` INT ( 2 ) DEFAULT 1,
|
||||||
roleCount: t.Dict[Roles, int]
|
werewolfes: int
|
||||||
#`werwolfzahl` INT ( 5 ) DEFAULT 0 ,
|
witches: int
|
||||||
#`hexenzahl` INT ( 5 ) DEFAULT 0 ,
|
seers: int
|
||||||
#`seherzahl` INT ( 5 ) DEFAULT 0 ,
|
hunters: int
|
||||||
#`jaegerzahl` INT ( 5 ) DEFAULT 0 ,
|
cupids: int
|
||||||
#`amorzahl` INT ( 2 ) DEFAULT 0 ,
|
protectors: int
|
||||||
#`beschuetzerzahl` INT ( 5 ) DEFAULT 0 ,
|
paranormals: int
|
||||||
#`parErmZahl` INT (5) DEFAULT 0 ,
|
lycantrophs: int
|
||||||
#`lykantrophenzahl` INT ( 5 ) DEFAULT 0 ,
|
spys: int
|
||||||
#`spionezahl` INT ( 5 ) DEFAULT 0 ,
|
murders: int
|
||||||
#`idiotenzahl` INT ( 5 ) DEFAULT 0 ,
|
pacifists: int
|
||||||
#`pazifistenzahl` INT ( 5 ) DEFAULT 0 ,
|
oldmans: int
|
||||||
#`altenzahl` INT ( 5 ) DEFAULT 0 ,
|
leaderwolfs: int
|
||||||
#`urwolfzahl` INT ( 5 ) DEFAULT 0 ,
|
|
||||||
randomSelect: bool #`zufaelligeAuswahl` INT ( 2 ) DEFAULT 0 ,
|
randomSelect: bool #`zufaelligeAuswahl` INT ( 2 ) DEFAULT 0 ,
|
||||||
randomBonus: int #`zufaelligeAuswahlBonus` INT ( 5 ) DEFAULT 0 ,
|
randomBonus: int #`zufaelligeAuswahlBonus` INT ( 5 ) DEFAULT 0 ,
|
||||||
unanimously: bool #`werwolfeinstimmig` INT ( 2 ) DEFAULT 1 ,
|
unanimously: bool #`werwolfeinstimmig` INT ( 2 ) DEFAULT 1 ,
|
||||||
|
timer_unanimously: int
|
||||||
|
timer_unanimously_per_wolf: int
|
||||||
|
timer_unsuccessfull: int
|
||||||
|
timer_unsuccessfull_per_wolf: int
|
||||||
|
timer_accusation: int
|
||||||
|
timer_accusation_per_player: int
|
||||||
|
timer_votation: int
|
||||||
|
timer_votation_per_player: int
|
||||||
|
timer_inactivity: int
|
||||||
|
timer_inactivity_per_player: int
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -74,6 +98,10 @@ class Player(object):
|
|||||||
diedInRound: int
|
diedInRound: int
|
||||||
accusedBy: "Player"
|
accusedBy: "Player"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def isWerwolf(self):
|
||||||
|
return Roles.isWerwolf(self.role)
|
||||||
|
|
||||||
|
|
||||||
# `wahlAuf` INT ( 5 ) DEFAULT -1 ,
|
# `wahlAuf` INT ( 5 ) DEFAULT -1 ,
|
||||||
# `angeklagtVon` INT ( 5 ) DEFAULT -1 ,
|
# `angeklagtVon` INT ( 5 ) DEFAULT -1 ,
|
||||||
@@ -92,29 +120,11 @@ class Player(object):
|
|||||||
# `countdownBis` INT (10) DEFAULT 0 ,
|
# `countdownBis` INT (10) DEFAULT 0 ,
|
||||||
# `countdownAb` INT (10) DEFAULT 0 ,
|
# `countdownAb` INT (10) DEFAULT 0 ,
|
||||||
|
|
||||||
|
class GameState(object):
|
||||||
class Game(object):
|
|
||||||
currentPhase: GamePhase #`spielphase` INT( 5 ) DEFAULT 0,
|
currentPhase: GamePhase #`spielphase` INT( 5 ) DEFAULT 0,
|
||||||
gameRound: int
|
gameRound: int
|
||||||
rules: Rules
|
rules: Rules
|
||||||
log: t.List[str]
|
log: t.List[str]
|
||||||
|
players: t.List[Player]
|
||||||
#`werwolfopfer` INT ( 5 ) DEFAULT -1 ,
|
#`werwolfopfer` INT ( 5 ) DEFAULT -1 ,
|
||||||
#`werwolftimer1` INT ( 10 ) DEFAULT 60 ,
|
|
||||||
#`werwolfzusatz1` INT ( 10 ) DEFAULT 4 ,
|
|
||||||
#`werwolftimer2` INT ( 10 ) DEFAULT 50 ,
|
|
||||||
#`werwolfzusatz2` INT ( 10 ) DEFAULT 3 ,
|
|
||||||
#`dorftimer` INT ( 10 ) DEFAULT 550 ,
|
|
||||||
#`dorfzusatz` INT ( 10 ) DEFAULT 10 ,
|
|
||||||
#`dorfstichwahltimer` INT ( 10 ) DEFAULT 200 ,
|
|
||||||
#`dorfstichwahlzusatz` INT ( 10 ) DEFAULT 5 ,
|
|
||||||
#`inaktivzeit` INT ( 10 ) DEFAULT 40 ,
|
|
||||||
#`inaktivzeitzusatz` INT ( 10 ) DEFAULT 0 ,
|
|
||||||
#`tagestext` TEXT ,
|
|
||||||
#`nacht` INT ( 5 ) DEFAULT 1 ,
|
|
||||||
#`log` LONGTEXT ,
|
|
||||||
#`list_lebe` LONGTEXT,
|
|
||||||
#`list_lebe_aktualisiert` BIGINT DEFAULT 0,
|
|
||||||
#`list_tot` LONGTEXT,
|
|
||||||
#`list_tot_aktualisiert` BIGINT DEFAULT 0,
|
|
||||||
#`waiting_for_others_time` BIGINT,
|
|
||||||
#`letzterAufruf` BIGINT
|
|
||||||
|
|||||||
@@ -54,12 +54,18 @@ texts = {
|
|||||||
models.Roles.Cupid: "Armor",
|
models.Roles.Cupid: "Armor",
|
||||||
models.Roles.Protector: "Beschützer/in",
|
models.Roles.Protector: "Beschützer/in",
|
||||||
models.Roles.ParanormalInvestigator: "Paranormaler Ermittler",
|
models.Roles.ParanormalInvestigator: "Paranormaler Ermittler",
|
||||||
models.Roles.Lycantrop: "Lykantroph/in",
|
models.Roles.Lycantroph: "Lykantroph/in",
|
||||||
models.Roles.Spy: "Spion/in",
|
models.Roles.Spy: "Spion/in",
|
||||||
models.Roles.Murder: "Mordlustige(r)",
|
models.Roles.Murder: "Mordlustige(r)",
|
||||||
models.Roles.Pacifist: "Pazifist/in",
|
models.Roles.Pacifist: "Pazifist/in",
|
||||||
models.Roles.OldMan: "Der/Die Alte",
|
models.Roles.OldMan: "Der/Die Alte",
|
||||||
models.Roles.Leaderwolf: "Urwolf",
|
models.Roles.Leaderwolf: "Urwolf",
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"role_group":{
|
||||||
|
"de": {
|
||||||
|
True : "Werwölfe",
|
||||||
|
False: "Dorfbewohner"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
23
setup.py
Normal file
23
setup.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import setuptools
|
||||||
|
|
||||||
|
with open("README.md", "r") as fh:
|
||||||
|
long_description = fh.read()
|
||||||
|
|
||||||
|
setuptools.setup(
|
||||||
|
name="pywerwolf",
|
||||||
|
version="0.0.1",
|
||||||
|
author="Matthias Bilger",
|
||||||
|
author_email="matthias@bilger.info",
|
||||||
|
description="A Python implementation of Werewolf/Mafia game",
|
||||||
|
long_description=long_description,
|
||||||
|
long_description_content_type="text/markdown",
|
||||||
|
url="https://github.com/m42e/pywerwolf",
|
||||||
|
license="GPL",
|
||||||
|
packages=setuptools.find_packages(), # Required
|
||||||
|
classifiers=[
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
|
"Operating System :: OS Independent",
|
||||||
|
'License :: Other/Proprietary License',
|
||||||
|
],
|
||||||
|
python_requires='>=3.6',
|
||||||
|
)
|
||||||
8
tests/test_game.py
Normal file
8
tests/test_game.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
import pywerwolf
|
||||||
|
import pywerwolf.gamelogic as gl
|
||||||
|
|
||||||
|
def test_game_init():
|
||||||
|
g = gl.Game()
|
||||||
|
|
||||||
Reference in New Issue
Block a user