diff --git a/.gitignore b/.gitignore
index 8b972d9..69cde2e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -100,6 +100,6 @@ ENV/
# mypy
.mypy_cache/
-#pytest
+# pytest
.pytest_cache
.idea/
diff --git a/.travis.yml b/.travis.yml
index 4d285ec..93754e9 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -20,4 +20,5 @@ deploy:
on:
branch: master
python: '3.6'
+ # tags: true # only deploys it when the commit is tagged
skip_existing: true
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index d03affe..dc8071c 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,7 @@
MIT License
Copyright (c) 2017 Samuel Kurath
+Copyright (c) 2018 Joel Niklaus
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 919a81a..72fdd00 100644
--- a/README.md
+++ b/README.md
@@ -1,28 +1,28 @@
-[](https://travis-ci.org/Murthy10/pyschieber)
+[](https://travis-ci.org/JoelNiklaus/schieber)
-# pyschieber
-Pyschieber is an implementation of the well known Swiss Schieber Jass game.
+# schieber
+Schieber is an implementation of the well known Swiss Schieber Jass game.
As OpenAI Gym provides APIs for several popular games to learn your algorithms master these games.
-Pyschieber aims to offer an API in the same manner.
+Schieber aims to offer an API in the same manner.
## Usage
-To install pyschieber, simply:
+To install schieber, simply:
```bash
-pip install pyschieber
+pip install schieber
```
-pyschieber officially supports Python 3.4, 3.5, 3.6, 3.7, 3.5-dev, 3.6-dev, 3.7-dev, nightly and PyPy3.
+schieber officially supports Python 3.4, 3.5, 3.6, 3.7, 3.5-dev, 3.6-dev, 3.7-dev, nightly and PyPy3.
### CLI :computer:
-Beside of the API, pyschieber provides a CLI client to play the funny Scheiber Jass game.
+Beside of the API, schieber provides a CLI client to play the funny Scheiber Jass game.
Currently your opponent will be a bot choosing a random card.
-After the pip installation you could run the ```pyschieber``` command on the console to play a game:
+After the pip installation you could run the ```schieber``` command on the console to play a game:
```bash
-$ pyschieber
+$ schieber
Tournament starts, the goal are 1500 points.
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Round 1 starts.
@@ -56,34 +56,34 @@ Please chose the trumpf by the number from 0 to 6:
### Jass Challenge
The usage of a CLI to play Schieber Jass could be boring.
-Therefore pyschieber provides a wrapper for your bots to play on the Zühlke Jass Server.
+Therefore schieber provides a wrapper for your bots to play on the Zühlke Jass Server.
-The [ServerPlayer](pyschieber/player/server_player/server_player.py) takes a pyschieber conform player
-An example how to launch is provide under [Server Launcher](pyschieber/example/server_launcher.py).
+The [ServerPlayer](schieber/player/server_player/server_player.py) takes a schieber conform player
+An example how to launch is provide under [Server Launcher](schieber/example/server_launcher.py).
For further information have a look at:
* https://github.com/webplatformz/challenge
* https://github.com/jakeret/elbotto
## API :clipboard:
-The idea of pyschieber is to extend the game with your own implemented player.
-Henc schieber provides entry points to fulfill this requirement.
+The idea of schieber is to extend the game with your own implemented player.
+Hence schieber provides entry points to fulfill this requirement.
-## Environemnt introduction
-To get a first feeling for the pyschieber playground let's have a look at a runable example.
+## Environment introduction
+To get a first feeling for the schieber playground let's have a look at a runable example.
1. The first thing you have to do, is to instantiate a new Tournament.
```python
-from pyschieber.tournament import Tournament
+from schieber.tournament import Tournament
tournament = Tournament(point_limit=1500)
```
2. Add the players to your tournament. In our example we use the erratic RandomPlayers Tick, Trick, Track and the GreedyPlayer Dagobert.
```python
-from pyschieber.player.random_player import RandomPlayer
-from pyschieber.player.greedy_player.greedy_player import GreedyPlayer
+from schieber.player.random_player import RandomPlayer
+from schieber.player.greedy_player.greedy_player import GreedyPlayer
players = [RandomPlayer(name='Tick'), RandomPlayer(name='Trick'),
@@ -117,8 +117,8 @@ To get more familiar with this concept let's have a look at the already mentione
```python
import random
-from pyschieber.player.base_player import BasePlayer
-from pyschieber.trumpf import Trumpf
+from schieber.player.base_player import BasePlayer
+from schieber.trumpf import Trumpf
class RandomPlayer(BasePlayer):
@@ -142,11 +142,12 @@ What's going on here?
The Random Player is pretty naive and he simply chooses randomly a card or a trumpf from the list of choices.
If the turn is not allowed he randomly chooses a new one until the rules of Schieber are satisfied.
-Other player examples are the [GreedyPlayer](pyschieber/player/greedy_player/greedy_player.py) or the [CliPlayer](pyschieber/player/cli_player.py).
+Other player examples are the [GreedyPlayer](schieber/player/greedy_player/greedy_player.py) or the [CliPlayer](schieber/player/cli_player.py).
Now you should be ready to get your hands dirty to implement your own player and beat the random players Tick, Trick and Track! :trophy:
## Enhancements
* Add Wiesen to the game
* Beautify the CLI :trollface:
-* Provide a simple network player
\ No newline at end of file
+* Provide a simple network player
+* Implement Matschbonus!
\ No newline at end of file
diff --git a/bin/pyschieber b/bin/schieber
similarity index 84%
rename from bin/pyschieber
rename to bin/schieber
index f3e37e3..028d56b 100644
--- a/bin/pyschieber
+++ b/bin/schieber
@@ -2,11 +2,11 @@
import sys, signal, argparse, logging
-from pyschieber.player.cli_player import CliPlayer
-from pyschieber.player.challenge_player.challenge_player import ChallengePlayer
-from pyschieber.player.greedy_player.greedy_player import GreedyPlayer
-from pyschieber.player.random_player import RandomPlayer
-from pyschieber.tournament import Tournament
+from schieber.player.cli_player import CliPlayer
+from schieber.player.challenge_player.challenge_player import ChallengePlayer
+from schieber.player.greedy_player.greedy_player import GreedyPlayer
+from schieber.player.random_player import RandomPlayer
+from schieber.tournament import Tournament
def parse_player_choice(player_choice=1, name_suffix=''):
@@ -36,14 +36,14 @@ def set_logging():
def handler(signum, frame):
- print('\n pyschieber terminated! Goodbye!')
+ print('\n schieber terminated! Goodbye!')
sys.exit(0)
if __name__ == "__main__":
set_logging()
signal.signal(signal.SIGINT, handler)
- parser = argparse.ArgumentParser(description='CLI pyschieber', )
+ parser = argparse.ArgumentParser(description='CLI schieber', )
parser.add_argument('-p', '--points', dest='points', type=int, help='Tournament points')
parser.add_argument('-f', '--first_opponent', dest='first_opponent', type=int,
help='Chose your first opponent. (1: Random player, 2: Greedy player, 3: Challenge player)')
diff --git a/pyschieber/card.py b/pyschieber/card.py
deleted file mode 100644
index 32b310d..0000000
--- a/pyschieber/card.py
+++ /dev/null
@@ -1,58 +0,0 @@
-import re
-
-from pyschieber.suit import Suit
-
-
-class Card:
- names = {6: '6', 7: '7', 8: '8', 9: '9', 10: 'Banner', 11: 'Under', 12: 'Ober', 13: 'Koennig', 14: 'Ass'}
- trumpf_rank = {6: 6, 7: 7, 8: 8, 10: 10, 12: 12, 13: 13, 14: 14, 9: 15, 11: 16}
- format_string = '<{0}:{1}>'
-
- def __init__(self, suit, value):
- self.suit = suit
- self.value = value
-
- def __lt__(self, other):
- return self.value < other.value
-
- def __eq__(self, other):
- return self.suit == other.suit and self.value == other.value
-
- def __hash__(self):
- return hash(str(self))
-
- def __str__(self):
- name = str(self.value)
- if self.value > 9:
- name = Card.names[self.value]
- return self.format_string.format(self.suit.name, name)
-
- def __repr__(self):
- return str(self)
-
- def get_trumpf_rank(self):
- return self.trumpf_rank[self.value]
-
- def is_higher_trumpf_than(self, other):
- return self.trumpf_rank > other.trumpf_rank
-
- def is_higher_than(self, other):
- return self.suit == other.suit and self.value > other.value
-
- def get_score(self, trumpf):
- if trumpf.name == self.suit.name:
- return 50 + self.get_trumpf_rank()
- else:
- return self.value
-
-
-def from_string_to_card(card_string):
- regex = re.sub(r'{(.+?)}', r'(?P<_\1>.+)', Card.format_string)
- values = list(re.search(regex, card_string).groups())
- suit = Suit[values[0]]
- card_value = ''
- for key, value in Card.names.items():
- if value == values[1]:
- card_value = key
- break
- return Card(suit=suit, value=card_value)
diff --git a/pyschieber/dealer.py b/pyschieber/dealer.py
deleted file mode 100644
index a8b89ad..0000000
--- a/pyschieber/dealer.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from random import shuffle
-
-from pyschieber.deck import Deck
-
-
-class Dealer:
- def __init__(self, players):
- self.players = players
- self.deck = Deck()
-
- def shuffle_cards(self):
- shuffle(self.deck.cards)
-
- def deal_cards(self):
- for i, card in enumerate(self.deck.cards):
- self.players[i % 4].set_card(card=card)
diff --git a/pyschieber/game.py b/pyschieber/game.py
deleted file mode 100644
index b86373c..0000000
--- a/pyschieber/game.py
+++ /dev/null
@@ -1,110 +0,0 @@
-import logging
-
-from pyschieber.dealer import Dealer
-from pyschieber.rules.stich_rules import stich_rules, card_allowed
-from pyschieber.rules.trumpf_rules import trumpf_allowed
-from pyschieber.rules.count_rules import count_stich, counting_factor
-from pyschieber.stich import PlayedCard, stich_dict, played_cards_dict
-from pyschieber.trumpf import Trumpf
-
-logger = logging.getLogger(__name__)
-
-
-class Game:
- def __init__(self, teams=None, point_limit=1500, use_counting_factor=True):
- self.teams = teams
- self.point_limit = point_limit
- self.players = [teams[0].players[0], teams[1].players[0], teams[0].players[1], teams[1].players[1]]
- self.dealer = Dealer(players=self.players)
- self.geschoben = False
- self.trumpf = None
- self.stiche = []
- self.cards_on_table = []
- self.use_counting_factor = use_counting_factor
-
- def play(self, start_player_index=0, whole_rounds=False):
- self.dealer.shuffle_cards()
- self.dealer.deal_cards()
- self.define_trumpf(start_player_index=start_player_index)
- logger.info('Chosen Trumpf: {0} \n'.format(self.trumpf.name))
- for i in range(9):
- stich = self.play_stich(start_player_index)
- self.count_points(stich, last=(i == 8))
- logger.info('\nStich: {0} \n'.format(stich.player))
- logger.info('{}{}\n'.format('-' * 180, self.trumpf))
- start_player_index = self.players.index(stich.player)
- self.stiche.append(stich)
- self.stich_over_information()
- if (self.teams[0].won(self.point_limit) or self.teams[1].won(self.point_limit)) and not whole_rounds:
- return True
- return False
-
- def define_trumpf(self, start_player_index):
- is_allowed_trumpf = False
- generator = self.players[start_player_index].choose_trumpf(geschoben=self.geschoben)
- chosen_trumpf = next(generator)
- if chosen_trumpf == Trumpf.SCHIEBEN:
- self.geschoben = True
- generator = self.players[(start_player_index + 2) % 4].choose_trumpf(geschoben=self.geschoben)
- chosen_trumpf = next(generator)
- while not is_allowed_trumpf:
- is_allowed_trumpf = trumpf_allowed(chosen_trumpf=chosen_trumpf, geschoben=self.geschoben)
- trumpf = generator.send(is_allowed_trumpf)
- chosen_trumpf = chosen_trumpf if trumpf is None else trumpf
- self.trumpf = chosen_trumpf
-
- def play_stich(self, start_player_index):
- self.cards_on_table = []
- first_card = self.play_card(table_cards=self.cards_on_table, player=self.players[start_player_index])
- self.move_made(self.players[start_player_index].id, first_card)
- self.cards_on_table = [PlayedCard(player=self.players[start_player_index], card=first_card)]
- for i in get_player_index(start_index=start_player_index):
- current_player = self.players[i]
- card = self.play_card(table_cards=self.cards_on_table, player=current_player)
- self.move_made(current_player.id, card)
- self.cards_on_table.append(PlayedCard(player=current_player, card=card))
- stich = stich_rules[self.trumpf](played_cards=self.cards_on_table)
- return stich
-
- def play_card(self, table_cards, player):
- cards = [played_card.card for played_card in table_cards]
- is_allowed_card = False
- generator = player.choose_card(state=self.get_status())
- chosen_card = next(generator)
- while not is_allowed_card:
- is_allowed_card = card_allowed(table_cards=cards, chosen_card=chosen_card, hand_cards=player.cards,
- trumpf=self.trumpf)
- card = generator.send(is_allowed_card)
- chosen_card = chosen_card if card is None else card
- else:
- logger.info('Table: {0}:{1}'.format(player, chosen_card))
- player.cards.remove(chosen_card)
- return chosen_card
-
- def move_made(self, player_id, card):
- for player in self.players:
- player.move_made(player_id, card, self.get_status())
-
- def stich_over_information(self):
- [player.stich_over(state=self.get_status()) for player in self.players]
-
- def count_points(self, stich, last):
- stich_player_index = self.players.index(stich.player)
- cards = [played_card.card for played_card in stich.played_cards]
- self.add_points(team_index=(stich_player_index % 2), cards=cards, last=last)
-
- def add_points(self, team_index, cards, last):
- points = count_stich(cards, self.trumpf, last=last)
- points = points * counting_factor[self.trumpf] if self.use_counting_factor else points
- self.teams[team_index].points += points
-
- def get_status(self):
- return dict(stiche=[stich_dict(stich) for stich in self.stiche], trumpf=self.trumpf.name,
- geschoben=self.geschoben, point_limit=self.point_limit,
- table=[played_cards_dict(played_card) for played_card in self.cards_on_table],
- teams=[dict(points=team.points) for team in self.teams])
-
-
-def get_player_index(start_index):
- for i in range(1, 4):
- yield (i + start_index) % 4
diff --git a/pyschieber/player/base_player.py b/pyschieber/player/base_player.py
deleted file mode 100644
index 1b974b0..0000000
--- a/pyschieber/player/base_player.py
+++ /dev/null
@@ -1,39 +0,0 @@
-import inspect
-
-from pyschieber.card import from_string_to_card
-from pyschieber.trumpf import Trumpf
-from pyschieber.rules.stich_rules import allowed_cards
-
-
-class BasePlayer:
- def __init__(self, name='unknown'):
- self.name = name
- self.cards = []
- self.trumpf_list = list(Trumpf)
- self.id = None
-
- def get_dict(self):
- return dict(name=self.name, type=type(self).__name__)
-
- def set_card(self, card):
- self.cards.append(card)
-
- def choose_trumpf(self, geschoben):
- raise NotImplementedError(str(inspect.stack()[1][3]))
-
- def choose_card(self, state=None):
- raise NotImplementedError(str(inspect.stack()[1][3]))
-
- def move_made(self, player_id, card, state):
- pass
-
- def stich_over(self, state=None):
- pass
-
- def allowed_cards(self, state):
- table_cards = [from_string_to_card(entry['card']) for entry in state['table']]
- trumpf = Trumpf[state['trumpf']]
- return allowed_cards(hand_cards=self.cards, table_cards=table_cards, trumpf=trumpf)
-
- def __str__(self):
- return ''.format(self.name)
diff --git a/pyschieber/player/random_player.py b/pyschieber/player/random_player.py
deleted file mode 100644
index f713ba9..0000000
--- a/pyschieber/player/random_player.py
+++ /dev/null
@@ -1,22 +0,0 @@
-import random
-
-from pyschieber.player.base_player import BasePlayer
-from pyschieber.trumpf import Trumpf
-
-
-class RandomPlayer(BasePlayer):
- def choose_trumpf(self, geschoben):
- return move(choices=list(Trumpf))
-
- def choose_card(self, state=None):
- cards = self.allowed_cards(state=state)
- return move(choices=cards)
-
-
-def move(choices):
- allowed = False
- while not allowed:
- choice = random.choice(choices)
- allowed = yield choice
- if allowed:
- yield None
diff --git a/pyschieber/rules/trumpf_rules.py b/pyschieber/rules/trumpf_rules.py
deleted file mode 100644
index 68c72b1..0000000
--- a/pyschieber/rules/trumpf_rules.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from pyschieber.trumpf import Trumpf
-
-
-def trumpf_allowed(chosen_trumpf, geschoben):
- return not (chosen_trumpf == Trumpf.SCHIEBEN and geschoben)
diff --git a/pyschieber/stich.py b/pyschieber/stich.py
deleted file mode 100644
index f221406..0000000
--- a/pyschieber/stich.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from collections import namedtuple
-
-PlayedCard = namedtuple('PlayedCard', ['player', 'card'])
-Stich = namedtuple('Stich', ['player', 'played_cards', 'trumpf'])
-
-
-def played_cards_dict(played_card):
- return {
- 'player_id': played_card.player.id,
- 'card': str(played_card.card)
- }
-
-
-def stich_dict(stich):
- return {
- 'player_id': stich.player.id,
- 'trumpf': stich.trumpf.name,
- 'played_cards': [played_cards_dict(played_card) for played_card in stich.played_cards]
- }
diff --git a/pyschieber/suit.py b/pyschieber/suit.py
deleted file mode 100644
index bc2f3e6..0000000
--- a/pyschieber/suit.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from enum import Enum
-
-Suit = Enum('Suit',['ROSE', 'BELL', 'ACORN', 'SHIELD'])
\ No newline at end of file
diff --git a/pyschieber/team.py b/pyschieber/team.py
deleted file mode 100644
index 7fd6b98..0000000
--- a/pyschieber/team.py
+++ /dev/null
@@ -1,13 +0,0 @@
-class Team:
- def __init__(self, players=None):
- self.points = 0
- self.players = players
-
- def player_by_number(self, number):
- for player in self.players:
- if player.number == number:
- return player
- return None
-
- def won(self, point_limit):
- return self.points >= point_limit
diff --git a/release.sh b/release.sh
new file mode 100755
index 0000000..53754e0
--- /dev/null
+++ b/release.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+# Check if version number has been incremented!
+
+rm -rf dist # Clean dist folder
+python3 setup.py sdist bdist_wheel # Build package
+gpg --detach-sign -a dist/* # Sign package
+twine upload dist/* # Upload package and signature
\ No newline at end of file
diff --git a/pyschieber/__init__.py b/schieber/__init__.py
similarity index 100%
rename from pyschieber/__init__.py
rename to schieber/__init__.py
diff --git a/schieber/card.py b/schieber/card.py
new file mode 100644
index 0000000..34871c5
--- /dev/null
+++ b/schieber/card.py
@@ -0,0 +1,160 @@
+import math
+import re
+
+import numpy as np
+
+from schieber.suit import Suit
+
+
+class Card:
+ """
+ Defines a card used in the game of Jassen.
+ """
+ names = {6: '6', 7: '7', 8: '8', 9: '9', 10: 'Banner', 11: 'Under', 12: 'Ober', 13: 'Koennig', 14: 'Ass'}
+ trumpf_rank = {6: 6, 7: 7, 8: 8, 10: 10, 12: 12, 13: 13, 14: 14, 9: 15, 11: 16}
+ format_string = '<{0}:{1}>'
+
+ def __init__(self, suit, value):
+ self.suit = suit
+ self.value = value
+
+ def __lt__(self, other):
+ return self.value < other.value
+
+ def __eq__(self, other):
+ return self.suit == other.suit and self.value == other.value
+
+ def __hash__(self):
+ return hash(str(self))
+
+ def __str__(self):
+ name = str(self.value)
+ if self.value > 9:
+ name = Card.names[self.value]
+ return self.format_string.format(self.suit.name, name)
+
+ def __repr__(self):
+ return str(self)
+
+ def get_trumpf_rank(self):
+ return self.trumpf_rank[self.value]
+
+ def is_higher_trumpf_than(self, other):
+ return self.trumpf_rank > other.trumpf_rank
+
+ def is_higher_than(self, other):
+ return self.suit == other.suit and self.value > other.value
+
+ def get_score(self, trumpf):
+ if trumpf.name == self.suit.name:
+ return 50 + self.get_trumpf_rank()
+ else:
+ return self.value
+
+
+# TODO transform these methods to @staticmethod in order to eliminate unnecessarily long imports
+
+def from_card_to_string(card):
+ return str(card)
+
+
+def from_string_to_card(card_string):
+ """
+ Converts a string representation of a card back to a card object.
+ :param card_string:
+ :return:
+ """
+ regex = re.sub(r'{(.+?)}', r'(?P<_\1>.+)', Card.format_string)
+ values = list(re.search(regex, card_string).groups())
+ suit = Suit[values[0]]
+ card_value = ''
+ for key, value in Card.names.items():
+ if value == values[1]:
+ card_value = key
+ break
+ return Card(suit=suit, value=card_value)
+
+
+def from_card_to_tuple(card):
+ return card.suit.value, card.value - 6
+
+
+def from_tuple_to_card(card_tuple):
+ return Card(suit=Suit(card_tuple[0]), value=card_tuple[1] + 6)
+
+
+def from_string_to_tuple(card_string):
+ card = from_string_to_card(card_string)
+ return from_card_to_tuple(card)
+
+
+def from_tuple_to_string(card_tuple):
+ card = from_tuple_to_card(card_tuple)
+ return from_card_to_string(card)
+
+
+def from_string_to_index(card_string):
+ card = from_string_to_card(card_string)
+ return from_card_to_index(card)
+
+
+def from_index_to_string(card_index):
+ card = from_index_to_card(card_index)
+ return from_card_to_string(card)
+
+
+def from_index_to_card(card_index):
+ """
+ The index is a number between 1 and 36 representing a card in the following way:
+ SUIT 6 7 8 9 Banner Under Ober Koennig Ass
+ ROSE 1 2 3 4 5 6 7 8 9
+ BELL 10 11 12 13 14 15 16 17 18
+ ACORN 19 20 21 22 23 24 25 26 27
+ SHIELD 28 29 30 31 32 33 34 35 36
+ An index of 0 denotes an empty Card --> None
+ :param card_index:
+ :return:
+ """
+ assert 0 <= card_index <= 36
+ if card_index == 0:
+ return None
+ return Card(suit=Suit(_get_suit(card_index)), value=_get_value(card_index))
+
+
+def from_card_to_index(card):
+ if card is None:
+ return 0
+ return card.value + card.suit.value * 9 - 5
+
+
+def from_card_to_onehot(card):
+ suit_onehot = 4 * [0]
+ suit_onehot[card.suit.value] = 1
+ value_onehot = 9 * [0]
+ value_onehot[card.value - 6] = 1
+ return suit_onehot + value_onehot
+
+
+def from_onehot_to_card(card_onehot):
+ ones = np.where(np.array(card_onehot) == 1)[0] # gets the indices where there is a one in the vector
+ suit = ones[0] # suit encoded in the first 4 bits (index 0 to 3)
+ value = ones[1] - 4 + 6 # value encoded in the following 9 bits (index 4 to 12)
+ return Card(Suit(suit), value)
+
+
+def from_onehot_to_string(card_onehot):
+ card = from_onehot_to_card(card_onehot)
+ return from_card_to_string(card)
+
+
+def from_string_to_onehot(card_string):
+ card = from_string_to_card(card_string)
+ return from_card_to_onehot(card)
+
+
+def _get_suit(card_index):
+ return int(math.floor(card_index / 9.1))
+
+
+def _get_value(card_index):
+ return card_index - _get_suit(card_index) * 9 + 5
diff --git a/schieber/dealer.py b/schieber/dealer.py
new file mode 100644
index 0000000..e56b629
--- /dev/null
+++ b/schieber/dealer.py
@@ -0,0 +1,26 @@
+import random
+
+from schieber.deck import Deck
+
+
+class Dealer:
+ def __init__(self, players):
+ self.players = players
+ self.deck = Deck()
+
+ def shuffle_cards(self, seed=None):
+ """
+ Shuffles the cards to a random ordering.
+ :param seed
+ :return:
+ """
+ random.seed(seed)
+ random.shuffle(self.deck.cards)
+
+ def deal_cards(self):
+ """
+ Deals 9 cards for every one of the 4 players participating in the game.
+ :return:
+ """
+ for i, card in enumerate(self.deck.cards):
+ self.players[i % 4].set_card(card=card)
diff --git a/pyschieber/deck.py b/schieber/deck.py
similarity index 54%
rename from pyschieber/deck.py
rename to schieber/deck.py
index da1b438..502c446 100644
--- a/pyschieber/deck.py
+++ b/schieber/deck.py
@@ -1,9 +1,12 @@
-from pyschieber.suit import Suit
-from pyschieber.card import Card
+from schieber.suit import Suit
+from schieber.card import Card
class Deck:
def __init__(self):
+ """
+ Initializes a deck of cards used for Jassen (from 6 to 10, Jack, Queen, King and Ace; each card in 4 suits)
+ """
self.cards = []
for suit in Suit:
self.cards += [Card(suit=suit, value=i) for i in range(6, 15)]
diff --git a/pyschieber/example/__init__.py b/schieber/example/__init__.py
similarity index 100%
rename from pyschieber/example/__init__.py
rename to schieber/example/__init__.py
diff --git a/pyschieber/example/main.py b/schieber/example/main.py
similarity index 59%
rename from pyschieber/example/main.py
rename to schieber/example/main.py
index ef361ef..8585710 100644
--- a/pyschieber/example/main.py
+++ b/schieber/example/main.py
@@ -1,7 +1,7 @@
-from pyschieber.player.challenge_player.challenge_player import ChallengePlayer
-from pyschieber.player.greedy_player.greedy_player import GreedyPlayer
-from pyschieber.player.random_player import RandomPlayer
-from pyschieber.tournament import Tournament
+from schieber.player.challenge_player.challenge_player import ChallengePlayer
+from schieber.player.greedy_player.greedy_player import GreedyPlayer
+from schieber.player.random_player import RandomPlayer
+from schieber.tournament import Tournament
def start_tournament(points):
diff --git a/pyschieber/example/server_launcher.py b/schieber/example/server_launcher.py
similarity index 90%
rename from pyschieber/example/server_launcher.py
rename to schieber/example/server_launcher.py
index ec71511..026607a 100644
--- a/pyschieber/example/server_launcher.py
+++ b/schieber/example/server_launcher.py
@@ -2,8 +2,8 @@
import logging
from multiprocessing import Process
-from pyschieber.player.greedy_player.greedy_player import GreedyPlayer
-from pyschieber.player.server_player.server_player import ServerPlayer
+from schieber.player.greedy_player.greedy_player import GreedyPlayer
+from schieber.player.server_player.server_player import ServerPlayer
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s', )
diff --git a/schieber/game.py b/schieber/game.py
new file mode 100644
index 0000000..736fb5b
--- /dev/null
+++ b/schieber/game.py
@@ -0,0 +1,222 @@
+import logging
+from multiprocessing import Condition
+
+from schieber.dealer import Dealer
+from schieber.rules.stich_rules import stich_rules, card_allowed
+from schieber.rules.trumpf_rules import trumpf_allowed
+from schieber.rules.count_rules import count_stich, counting_factor
+from schieber.stich import PlayedCard, stich_dict, played_card_dict
+from schieber.trumpf import Trumpf
+
+logger = logging.getLogger(__name__)
+
+
+class Game:
+ def __init__(self, teams=None, point_limit=1500, use_counting_factor=False, seed=None):
+ self.teams = teams
+ self.point_limit = point_limit
+ self.players = [teams[0].players[0], teams[1].players[0], teams[0].players[1], teams[1].players[1]]
+ self.dealer = Dealer(players=self.players)
+ self.geschoben = False
+ self.trumpf = None
+ self.stiche = []
+ self.cards_on_table = []
+ self.use_counting_factor = use_counting_factor
+ self.seed = seed
+ self.endless_play_control = Condition() # used to control the termination of the play_endless method
+ self.stop_playing = False # has to be set to true in order to stop the endless play
+
+ def play_endless(self, start_player_index=0, whole_rounds=True):
+ """
+ Plays one game after the other with no end. This can be used for training a RL Player. Like this we can reuse
+ one game. When we are training with tournaments, each time we play a game, it is added to the list of games.
+ This could result in very high unneeded memory usage.
+ :param start_player_index:
+ :param whole_rounds:
+ :return:
+ """
+ while True:
+ logger.debug("start playing game")
+ self.play(start_player_index, whole_rounds)
+ logger.debug("game finished")
+ try:
+ self.endless_play_control.acquire()
+ # timeout in case something goes wrong in the reset, or reset is not called for any reason.
+ # In the normal case we just want to continue playing
+ received = self.endless_play_control.wait(0.01)
+ if received:
+ logger.debug("endless play received control message")
+ else:
+ logger.debug(
+ "endless play did not receive control message! Timeout occurred. Endless play resuming.")
+ if self.stop_playing:
+ logger.debug("stopping endless play")
+ break
+ finally:
+ self.endless_play_control.release()
+ logger.debug("reset game")
+ self.reset()
+
+ def reset(self):
+ """
+ Resets the game so that a new game can be started. Used in the endless mode
+ :return:
+ """
+ self.reset_points()
+ self.stiche = []
+
+ def play(self, start_player_index=0, whole_rounds=False):
+ """
+ Plays a game from the start to the end in the following manner:
+ 1. The dealer shuffles the cards
+ 2. The dealer deals 9 cards to each player
+ 3. The player on the right side of the dealer chooses the trumpf. If he/she chooses 'geschoben' his/her partner
+ can choose the trumpf.
+ 4. For 9 rounds/stichs let the players play their cards.
+ 5. After each stich count the points, update the starting player based on who won the stich and add the cards
+ played in the stich to the already played stichs.
+ 6. Check if a team has reached the point limit
+ :param start_player_index:
+ :param whole_rounds:
+ :return:
+ """
+ if self.seed is not None:
+ # Increment seed by one so that each game is different.
+ # But still the sequence of games is the same each time
+ self.seed += 1
+ self.dealer.shuffle_cards(self.seed)
+ self.dealer.deal_cards()
+ self.define_trumpf(start_player_index=start_player_index)
+ logger.info('Chosen Trumpf: {0} \n'.format(self.trumpf.name))
+ for i in range(9):
+ stich = self.play_stich(start_player_index)
+ self.count_points(stich, last=(i == 8))
+ logger.info('\nStich: {0} \n'.format(stich.player))
+ logger.info('{}{}\n'.format('-' * 180, self.trumpf))
+ start_player_index = self.players.index(stich.player)
+ self.stiche.append(stich)
+ self.stich_over_information()
+ if (self.teams[0].won(self.point_limit) or self.teams[1].won(self.point_limit)) and not whole_rounds:
+ return True
+ return False
+
+ def define_trumpf(self, start_player_index):
+ """
+ Sets the trumpf based on the choice of the player assigned to choose the trumpf
+ :param start_player_index: The player which is on the right side of the dealer
+ :return:
+ """
+ is_allowed_trumpf = False
+ generator = self.players[start_player_index].choose_trumpf(geschoben=self.geschoben)
+ chosen_trumpf = next(generator)
+ if chosen_trumpf == Trumpf.SCHIEBEN:
+ self.geschoben = True
+ generator = self.players[(start_player_index + 2) % 4].choose_trumpf(geschoben=self.geschoben)
+ chosen_trumpf = next(generator)
+ while not is_allowed_trumpf:
+ is_allowed_trumpf = trumpf_allowed(chosen_trumpf=chosen_trumpf, geschoben=self.geschoben)
+ trumpf = generator.send(is_allowed_trumpf)
+ chosen_trumpf = chosen_trumpf if trumpf is None else trumpf
+ self.trumpf = chosen_trumpf
+ return self.trumpf
+
+ def play_stich(self, start_player_index):
+ """
+ Plays one entire stich
+ :param start_player_index: the index of the player who won the last stich or was assigned to choose the trumpf
+ :return: the stich containing the played cards and the winner
+ """
+ self.cards_on_table = []
+ first_card = self.play_card(table_cards=self.cards_on_table, player=self.players[start_player_index])
+ self.move_made(self.players[start_player_index].id, first_card)
+ self.cards_on_table = [PlayedCard(player=self.players[start_player_index], card=first_card)]
+ for i in get_player_index(start_index=start_player_index):
+ current_player = self.players[i]
+ card = self.play_card(table_cards=self.cards_on_table, player=current_player)
+ self.move_made(current_player.id, card)
+ self.cards_on_table.append(PlayedCard(player=current_player, card=card))
+ stich = stich_rules[self.trumpf](played_cards=self.cards_on_table)
+ return stich
+
+ def play_card(self, table_cards, player):
+ """
+ Checks if the card played by the player is allowed. If yes removes the card from the players hand.
+ :param table_cards:
+ :param player:
+ :return: the card chosen by the player
+ """
+ cards = [played_card.card for played_card in table_cards]
+ is_allowed_card = False
+ generator = player.choose_card(state=self.get_status())
+ chosen_card = next(generator)
+ while not is_allowed_card:
+ is_allowed_card = card_allowed(table_cards=cards, chosen_card=chosen_card, hand_cards=player.cards,
+ trumpf=self.trumpf)
+ card = generator.send(is_allowed_card)
+ chosen_card = chosen_card if card is None else card
+ else:
+ logger.info('Table: {0}:{1}'.format(player, chosen_card))
+ player.cards.remove(chosen_card)
+ return chosen_card
+
+ def move_made(self, player_id, card):
+ for player in self.players:
+ player.move_made(player_id, card, self.get_status())
+
+ def stich_over_information(self):
+ [player.stich_over(state=self.get_status()) for player in self.players]
+
+ def count_points(self, stich, last):
+ """
+ Gets the team of the winner of the stich and counts the points.
+ :param stich:
+ :param last: True if it is the last stich of the Game, False otherwise
+ :return:
+ """
+ stich_player_index = self.players.index(stich.player)
+ cards = [played_card.card for played_card in stich.played_cards]
+ self.add_points(team_index=(stich_player_index % 2), cards=cards, last=last)
+
+ def add_points(self, team_index, cards, last):
+ """
+ Adds the points of the cards to the score of the team who won the stich.
+ :param team_index:
+ :param cards:
+ :param last:
+ :return:
+ """
+ points = count_stich(cards, self.trumpf, last=last)
+ points = points * counting_factor[self.trumpf] if self.use_counting_factor else points
+ self.teams[team_index].points += points
+
+ def get_status(self):
+ """
+ Returns the status of the game in a dictionary containing
+ - the stiche
+ - the trumpf
+ - if it has been geschoben
+ - the point limit
+ - the cards currently on the table
+ - the teams
+ :return:
+ """
+ return dict(
+ stiche=[stich_dict(stich) for stich in self.stiche],
+ trumpf=self.trumpf.name,
+ geschoben=self.geschoben,
+ point_limit=self.point_limit,
+ table=[played_card_dict(played_card) for played_card in self.cards_on_table],
+ teams=[dict(points=team.points) for team in self.teams]
+ )
+
+ def reset_points(self):
+ """
+ Resets the points of the teams to 0. This is used when single games are played.
+ :return:
+ """
+ [team.reset_points() for team in self.teams]
+
+
+def get_player_index(start_index):
+ for i in range(1, 4):
+ yield (i + start_index) % 4
diff --git a/pyschieber/helpers/__init__.py b/schieber/helpers/__init__.py
similarity index 100%
rename from pyschieber/helpers/__init__.py
rename to schieber/helpers/__init__.py
diff --git a/pyschieber/helpers/game_helper.py b/schieber/helpers/game_helper.py
similarity index 93%
rename from pyschieber/helpers/game_helper.py
rename to schieber/helpers/game_helper.py
index 1429725..6d6e6f4 100644
--- a/pyschieber/helpers/game_helper.py
+++ b/schieber/helpers/game_helper.py
@@ -1,4 +1,4 @@
-from pyschieber.suit import Suit
+from schieber.suit import Suit
def split_card_values_by_suit(cards):
diff --git a/pyschieber/player/__init__.py b/schieber/player/__init__.py
similarity index 100%
rename from pyschieber/player/__init__.py
rename to schieber/player/__init__.py
diff --git a/schieber/player/base_player.py b/schieber/player/base_player.py
new file mode 100644
index 0000000..a1359e2
--- /dev/null
+++ b/schieber/player/base_player.py
@@ -0,0 +1,62 @@
+import inspect
+
+from schieber.card import from_string_to_card
+from schieber.trumpf import Trumpf
+from schieber.rules.stich_rules import allowed_cards
+
+
+class BasePlayer:
+ def __init__(self, name='unknown', seed=None, trumps='all'):
+ """
+
+ :param name:
+ :param seed:
+ :param trumps: if 'all': no restriction on trumps available, if 'obe_abe': only OBE_ABE allowed
+ """
+ self.name = name
+ self.cards = []
+ self.trumpf_list = list(Trumpf)
+ self.id = name
+ self.seed = seed
+ self.trumps = trumps
+
+ def get_dict(self):
+ """
+ Returns a dictionary containing:
+ - the name
+ - the type (RandomPlayer, GreedyPlayer, etc.)
+ :return:
+ """
+ return dict(name=self.name, type=type(self).__name__)
+
+ def set_card(self, card):
+ self.cards.append(card)
+
+ def choose_trumpf(self, geschoben):
+ raise NotImplementedError(str(inspect.stack()[1][3]))
+
+ def choose_card(self, state=None):
+ raise NotImplementedError(str(inspect.stack()[1][3]))
+
+ def move_made(self, player_id, card, state):
+ pass
+
+ def stich_over(self, state=None):
+ pass
+
+ def allowed_cards(self, state):
+ return self.allowed_cards_with_hand_cards(state, self.cards)
+
+ def allowed_cards_with_hand_cards(self, state, hand_cards):
+ """
+ Returns the cards on the hand of the player which he/she is allowed to play in the current state according to the rules
+ :param hand_cards:
+ :param state:
+ :return:
+ """
+ table_cards = [from_string_to_card(entry['card']) for entry in state['table']]
+ trumpf = Trumpf[state['trumpf']]
+ return allowed_cards(hand_cards=hand_cards, table_cards=table_cards, trumpf=trumpf)
+
+ def __str__(self):
+ return ''.format(self.name)
diff --git a/pyschieber/player/challenge_player/__init__.py b/schieber/player/challenge_player/__init__.py
similarity index 100%
rename from pyschieber/player/challenge_player/__init__.py
rename to schieber/player/challenge_player/__init__.py
diff --git a/pyschieber/player/challenge_player/challenge_player.py b/schieber/player/challenge_player/challenge_player.py
similarity index 77%
rename from pyschieber/player/challenge_player/challenge_player.py
rename to schieber/player/challenge_player/challenge_player.py
index 5205d65..0f1f982 100644
--- a/pyschieber/player/challenge_player/challenge_player.py
+++ b/schieber/player/challenge_player/challenge_player.py
@@ -1,8 +1,9 @@
import random
-from pyschieber.player.base_player import BasePlayer
-from pyschieber.player.challenge_player.strategy.jass_strategy import JassStrategy
-from pyschieber.card import Card
+from schieber.player.base_player import BasePlayer
+from schieber.player.challenge_player.strategy.jass_strategy import JassStrategy
+from schieber.card import Card
+from schieber.trumpf import Trumpf
class ChallengePlayer(BasePlayer):
@@ -15,7 +16,10 @@ def choose_trumpf(self, geschoben):
allowed = False
while not allowed:
trumpf = self.strategy.chose_trumpf(self.cards, geschoben)
- allowed = yield trumpf
+ if self.trumps == 'all':
+ allowed = yield trumpf
+ elif self.trumps == 'obe_abe':
+ allowed = yield Trumpf.OBE_ABE
if allowed:
yield None
@@ -42,11 +46,11 @@ def choose_card(self, state=None):
while not allowed:
card = self.strategy.choose_card(cards, state, self.role)
if not isinstance(card, Card):
+ random.seed(self.seed)
card = random.choice(cards)
allowed = yield card
if allowed:
yield None
-
def move_made(self, player_id, card, state):
self.strategy.move_made(player_id, card, state)
diff --git a/pyschieber/player/challenge_player/strategy/__init__.py b/schieber/player/challenge_player/strategy/__init__.py
similarity index 100%
rename from pyschieber/player/challenge_player/strategy/__init__.py
rename to schieber/player/challenge_player/strategy/__init__.py
diff --git a/pyschieber/player/challenge_player/strategy/card_counter.py b/schieber/player/challenge_player/strategy/card_counter.py
similarity index 90%
rename from pyschieber/player/challenge_player/strategy/card_counter.py
rename to schieber/player/challenge_player/strategy/card_counter.py
index 4a0ddd5..33e3c5a 100644
--- a/pyschieber/player/challenge_player/strategy/card_counter.py
+++ b/schieber/player/challenge_player/strategy/card_counter.py
@@ -1,16 +1,16 @@
-from pyschieber.player.challenge_player.strategy.mode.trumpf_color_mode import *
-from pyschieber.player.challenge_player.strategy.mode.top_down_mode import *
-from pyschieber.player.challenge_player.strategy.mode.bottom_up_mode import *
-from pyschieber.player.challenge_player.strategy.flags.doesnt_habe_card_flag import DoesntHaveCardFlag
-from pyschieber.player.challenge_player.strategy.flags.previously_had_stich_flag import PreviouslyHadStichFlag
-from pyschieber.player.challenge_player.strategy.flags.falied_to_serve_suit_flag import FailedToServeSuitFlag
-from pyschieber.player.challenge_player.strategy.flags.suit_verworfen_flag import SuitVerworfenFlag
-from pyschieber.deck import Deck
-from pyschieber.card import from_string_to_card
-from pyschieber.rules.stich_rules import stich_rules
-from pyschieber.trumpf import get_trumpf
-from pyschieber.stich import PlayedCard
-from pyschieber.suit import Suit
+from schieber.player.challenge_player.strategy.mode.trumpf_color_mode import *
+from schieber.player.challenge_player.strategy.mode.top_down_mode import *
+from schieber.player.challenge_player.strategy.mode.bottom_up_mode import *
+from schieber.player.challenge_player.strategy.flags.doesnt_habe_card_flag import DoesntHaveCardFlag
+from schieber.player.challenge_player.strategy.flags.previously_had_stich_flag import PreviouslyHadStichFlag
+from schieber.player.challenge_player.strategy.flags.falied_to_serve_suit_flag import FailedToServeSuitFlag
+from schieber.player.challenge_player.strategy.flags.suit_verworfen_flag import SuitVerworfenFlag
+from schieber.deck import Deck
+from schieber.card import from_string_to_card
+from schieber.rules.stich_rules import stich_rules
+from schieber.trumpf import get_trumpf
+from schieber.stich import PlayedCard
+from schieber.suit import Suit
from math import floor
diff --git a/pyschieber/player/challenge_player/strategy/flags/__init__.py b/schieber/player/challenge_player/strategy/flags/__init__.py
similarity index 100%
rename from pyschieber/player/challenge_player/strategy/flags/__init__.py
rename to schieber/player/challenge_player/strategy/flags/__init__.py
diff --git a/pyschieber/player/challenge_player/strategy/flags/doesnt_habe_card_flag.py b/schieber/player/challenge_player/strategy/flags/doesnt_habe_card_flag.py
similarity index 100%
rename from pyschieber/player/challenge_player/strategy/flags/doesnt_habe_card_flag.py
rename to schieber/player/challenge_player/strategy/flags/doesnt_habe_card_flag.py
diff --git a/pyschieber/player/challenge_player/strategy/flags/falied_to_serve_suit_flag.py b/schieber/player/challenge_player/strategy/flags/falied_to_serve_suit_flag.py
similarity index 100%
rename from pyschieber/player/challenge_player/strategy/flags/falied_to_serve_suit_flag.py
rename to schieber/player/challenge_player/strategy/flags/falied_to_serve_suit_flag.py
diff --git a/pyschieber/player/challenge_player/strategy/flags/previously_had_stich_flag.py b/schieber/player/challenge_player/strategy/flags/previously_had_stich_flag.py
similarity index 100%
rename from pyschieber/player/challenge_player/strategy/flags/previously_had_stich_flag.py
rename to schieber/player/challenge_player/strategy/flags/previously_had_stich_flag.py
diff --git a/pyschieber/player/challenge_player/strategy/flags/suit_verworfen_flag.py b/schieber/player/challenge_player/strategy/flags/suit_verworfen_flag.py
similarity index 100%
rename from pyschieber/player/challenge_player/strategy/flags/suit_verworfen_flag.py
rename to schieber/player/challenge_player/strategy/flags/suit_verworfen_flag.py
diff --git a/pyschieber/player/challenge_player/strategy/jass_strategy.py b/schieber/player/challenge_player/strategy/jass_strategy.py
similarity index 74%
rename from pyschieber/player/challenge_player/strategy/jass_strategy.py
rename to schieber/player/challenge_player/strategy/jass_strategy.py
index d55b944..661b9b5 100644
--- a/pyschieber/player/challenge_player/strategy/jass_strategy.py
+++ b/schieber/player/challenge_player/strategy/jass_strategy.py
@@ -1,8 +1,8 @@
-from pyschieber.player.challenge_player.strategy.mode.trumpf_color_mode import *
-from pyschieber.player.challenge_player.strategy.mode.top_down_mode import *
-from pyschieber.player.challenge_player.strategy.mode.bottom_up_mode import *
-from pyschieber.player.challenge_player.strategy.card_counter import *
-from pyschieber.trumpf import Trumpf
+from schieber.player.challenge_player.strategy.mode.trumpf_color_mode import *
+from schieber.player.challenge_player.strategy.mode.top_down_mode import *
+from schieber.player.challenge_player.strategy.mode.bottom_up_mode import *
+from schieber.player.challenge_player.strategy.card_counter import *
+from schieber.trumpf import Trumpf
class JassStrategy:
diff --git a/pyschieber/player/challenge_player/strategy/mode/__init__.py b/schieber/player/challenge_player/strategy/mode/__init__.py
similarity index 100%
rename from pyschieber/player/challenge_player/strategy/mode/__init__.py
rename to schieber/player/challenge_player/strategy/mode/__init__.py
diff --git a/pyschieber/player/challenge_player/strategy/mode/bottom_up_mode.py b/schieber/player/challenge_player/strategy/mode/bottom_up_mode.py
similarity index 95%
rename from pyschieber/player/challenge_player/strategy/mode/bottom_up_mode.py
rename to schieber/player/challenge_player/strategy/mode/bottom_up_mode.py
index d3c6f30..7fbec38 100644
--- a/pyschieber/player/challenge_player/strategy/mode/bottom_up_mode.py
+++ b/schieber/player/challenge_player/strategy/mode/bottom_up_mode.py
@@ -1,7 +1,7 @@
-from pyschieber.player.challenge_player.strategy.mode.uncolored_trumpf import UncoloredTrumpf
-from pyschieber.helpers.game_helper import *
-from pyschieber.trumpf import Trumpf
-from pyschieber.card import from_string_to_card
+from schieber.player.challenge_player.strategy.mode.uncolored_trumpf import UncoloredTrumpf
+from schieber.helpers.game_helper import *
+from schieber.trumpf import Trumpf
+from schieber.card import from_string_to_card
class BottomUpMode(UncoloredTrumpf):
diff --git a/pyschieber/player/challenge_player/strategy/mode/mode.py b/schieber/player/challenge_player/strategy/mode/mode.py
similarity index 94%
rename from pyschieber/player/challenge_player/strategy/mode/mode.py
rename to schieber/player/challenge_player/strategy/mode/mode.py
index 7b10717..00dea90 100644
--- a/pyschieber/player/challenge_player/strategy/mode/mode.py
+++ b/schieber/player/challenge_player/strategy/mode/mode.py
@@ -1,6 +1,6 @@
-from pyschieber.helpers.game_helper import *
-from pyschieber.card import from_string_to_card
-from pyschieber.trumpf import get_trumpf
+from schieber.helpers.game_helper import *
+from schieber.card import from_string_to_card
+from schieber.trumpf import get_trumpf
class Mode:
def is_bock(self, c, c_counter):
diff --git a/pyschieber/player/challenge_player/strategy/mode/top_down_mode.py b/schieber/player/challenge_player/strategy/mode/top_down_mode.py
similarity index 95%
rename from pyschieber/player/challenge_player/strategy/mode/top_down_mode.py
rename to schieber/player/challenge_player/strategy/mode/top_down_mode.py
index 69dc93f..2da3c9a 100644
--- a/pyschieber/player/challenge_player/strategy/mode/top_down_mode.py
+++ b/schieber/player/challenge_player/strategy/mode/top_down_mode.py
@@ -1,7 +1,7 @@
-from pyschieber.player.challenge_player.strategy.mode.uncolored_trumpf import UncoloredTrumpf
-from pyschieber.helpers.game_helper import *
-from pyschieber.trumpf import Trumpf
-from pyschieber.card import from_string_to_card
+from schieber.player.challenge_player.strategy.mode.uncolored_trumpf import UncoloredTrumpf
+from schieber.helpers.game_helper import *
+from schieber.trumpf import Trumpf
+from schieber.card import from_string_to_card
class TopDownMode(UncoloredTrumpf):
diff --git a/pyschieber/player/challenge_player/strategy/mode/trumpf_color_mode.py b/schieber/player/challenge_player/strategy/mode/trumpf_color_mode.py
similarity index 98%
rename from pyschieber/player/challenge_player/strategy/mode/trumpf_color_mode.py
rename to schieber/player/challenge_player/strategy/mode/trumpf_color_mode.py
index 1596d2a..b8210bb 100644
--- a/pyschieber/player/challenge_player/strategy/mode/trumpf_color_mode.py
+++ b/schieber/player/challenge_player/strategy/mode/trumpf_color_mode.py
@@ -1,7 +1,7 @@
-from pyschieber.player.challenge_player.strategy.mode.mode import Mode
-from pyschieber.helpers.game_helper import *
-from pyschieber.trumpf import Trumpf
-from pyschieber.card import from_string_to_card
+from schieber.player.challenge_player.strategy.mode.mode import Mode
+from schieber.helpers.game_helper import *
+from schieber.trumpf import Trumpf
+from schieber.card import from_string_to_card
class TrumpfColorMode(Mode):
diff --git a/pyschieber/player/challenge_player/strategy/mode/uncolored_trumpf.py b/schieber/player/challenge_player/strategy/mode/uncolored_trumpf.py
similarity index 98%
rename from pyschieber/player/challenge_player/strategy/mode/uncolored_trumpf.py
rename to schieber/player/challenge_player/strategy/mode/uncolored_trumpf.py
index 18ef441..b00503f 100644
--- a/pyschieber/player/challenge_player/strategy/mode/uncolored_trumpf.py
+++ b/schieber/player/challenge_player/strategy/mode/uncolored_trumpf.py
@@ -1,6 +1,6 @@
-from pyschieber.player.challenge_player.strategy.mode.mode import Mode
-from pyschieber.helpers.game_helper import *
-from pyschieber.card import from_string_to_card
+from schieber.player.challenge_player.strategy.mode.mode import Mode
+from schieber.helpers.game_helper import *
+from schieber.card import from_string_to_card
class UncoloredTrumpf(Mode):
diff --git a/pyschieber/player/cli_player.py b/schieber/player/cli_player.py
similarity index 97%
rename from pyschieber/player/cli_player.py
rename to schieber/player/cli_player.py
index bb2530a..1506783 100644
--- a/pyschieber/player/cli_player.py
+++ b/schieber/player/cli_player.py
@@ -1,4 +1,4 @@
-from pyschieber.player.base_player import BasePlayer
+from schieber.player.base_player import BasePlayer
class CliPlayer(BasePlayer):
diff --git a/schieber/player/external_player.py b/schieber/player/external_player.py
new file mode 100644
index 0000000..6d0ce59
--- /dev/null
+++ b/schieber/player/external_player.py
@@ -0,0 +1,136 @@
+import logging
+from multiprocessing import Condition
+
+from schieber.player.greedy_player.greedy_player import GreedyPlayer
+
+from schieber.player.challenge_player.challenge_player import ChallengePlayer
+
+from schieber.player.base_player import BasePlayer
+from schieber.trumpf import Trumpf
+
+logger = logging.getLogger(__name__)
+
+
+class ExternalPlayer(GreedyPlayer):
+ """
+ The RL player in the gym environment wants to initiate control by
+ invoking the step() function. This step function sends an action, lets the environment simulate and then
+ receives an observation back from the environment.
+ In this schieber environment the control is initiated by the Game and not by the player. This is why we need this
+ architecture with this external player. The external player blocks when its choose_card() method is called and
+ sends the current state received by the Game as an observation to the rl player from gym who connects via a
+ websocket. Then the rl agent selects an action and sends it back to this external player. The external player
+ submits this action as the chosen card to the Game. The Game simulates the game and this process starts over.
+ With the help of this architecture we can use the benefits of the standardized gym environments with many
+ rl methods which are already implemented (openai baselines: https://github.com/openai/baselines).
+ """
+
+ def __init__(self, name='unknown', seed=None, trumps='all'):
+ super().__init__(name, seed, trumps)
+ self.action_received = Condition()
+ self.observation_received = Condition()
+
+ self.action = {}
+ self.observation = {}
+
+ def choose_card(self, state=None):
+ """
+ Chooses the card and verifies if the chosen card is allowed to be played in the current game state.
+ :param state:
+ :return:
+ """
+ # if self.at_last_stich():
+ # allowed = yield self.cards[0]
+ # else:
+ self.observation_received.acquire()
+ self.observation = self.build_observation(state, self.cards)
+ logger.debug(f"choose_card received observation: {self.observation}")
+ self.observation_received.notify_all() # notify all threads to be sure
+ self.observation_received.release()
+
+ self.action_received.acquire()
+ received = self.action_received.wait()
+ if not received:
+ logger.debug("Timeout occurred. action_received condition has not been notified.")
+ logger.debug(f"choose_card received action: {self.action}")
+ allowed_cards = self.allowed_cards(state=state)
+ chosen_card = allowed_cards[0] # set chosen_card to the first allowed card in case anything goes south
+ chosen_card = self.set_chosen_card(allowed_cards, chosen_card)
+ self.action_received.release()
+
+ allowed = yield chosen_card
+
+ if allowed:
+ yield None
+
+ @staticmethod
+ def build_observation(state, cards):
+ observation = state
+ observation["cards"] = cards
+ return observation
+
+ def set_chosen_card(self, allowed_cards, chosen_card):
+ """
+ Sets the chosen card based on the action of the RL player.
+ :param allowed_cards:
+ :param chosen_card:
+ :return:
+ """
+ if self.action is not None:
+ if self.action in allowed_cards:
+ logger.info(f"Successfully chose the card: {self.action}")
+ chosen_card = self.action
+ else:
+ logger.error(f"{self.action} is not a valid card! Choosing the first allowed card now.")
+ else:
+ logger.debug("chosen card is None")
+ return chosen_card
+
+ def get_observation(self):
+ """
+ Gets the observation obtained by the game
+ :return:
+ """
+ self.observation_received.acquire()
+ received = self.observation_received.wait(0.01)
+ if not received:
+ print("Timeout occurred. observation_received condition has not been notified.")
+ observation = self.observation
+ logger.debug(f"get_observation {observation}")
+ self.observation_received.release()
+ return observation
+
+ def set_action(self, action):
+ """
+ Sets the action chosen by the RL player
+ :param action:
+ :return:
+ """
+ if self.hand_empty():
+ logger.error("set_action: There are no cards on my hand, so I cannot choose any card!")
+ self.action_received.acquire()
+ self.action = action
+ logger.debug(f"set_action: {self.action}")
+ self.action_received.notify_all() # notify all threads to be sure
+ self.action_received.release()
+
+ def before_first_stich(self):
+ """
+ Checks if the player has already played any cards in this game
+ :return:
+ """
+ return len(self.cards) == 9
+
+ def at_last_stich(self):
+ """
+ Checks if the player is at the last stich where there is no choice anymore
+ :return:
+ """
+ return len(self.cards) == 1
+
+ def hand_empty(self):
+ """
+ Checks if the hand is empty or if there are any cards left.
+ :return:
+ """
+ return len(self.cards) == 0
diff --git a/pyschieber/player/greedy_player/__init__.py b/schieber/player/greedy_player/__init__.py
similarity index 100%
rename from pyschieber/player/greedy_player/__init__.py
rename to schieber/player/greedy_player/__init__.py
diff --git a/pyschieber/player/greedy_player/greedy_player.py b/schieber/player/greedy_player/greedy_player.py
similarity index 69%
rename from pyschieber/player/greedy_player/greedy_player.py
rename to schieber/player/greedy_player/greedy_player.py
index 64e0a01..f8d9a9f 100644
--- a/pyschieber/player/greedy_player/greedy_player.py
+++ b/schieber/player/greedy_player/greedy_player.py
@@ -1,14 +1,17 @@
-from pyschieber.player.base_player import BasePlayer
-from pyschieber.player.greedy_player.trumpf_decision import choose_trumpf
-from pyschieber.trumpf import Trumpf
+from schieber.player.base_player import BasePlayer
+from schieber.player.greedy_player import trumpf_decision
+from schieber.trumpf import Trumpf
class GreedyPlayer(BasePlayer):
def choose_trumpf(self, geschoben):
allowed = False
while not allowed:
- trumpf, _ = choose_trumpf(cards=self.cards, geschoben=geschoben)
- allowed = yield trumpf
+ trumpf, _ = trumpf_decision.choose_trumpf(cards=self.cards, geschoben=geschoben)
+ if self.trumps == 'all':
+ allowed = yield trumpf
+ elif self.trumps == 'obe_abe':
+ allowed = yield Trumpf.OBE_ABE
if allowed:
yield None
diff --git a/pyschieber/player/greedy_player/trumpf_decision.py b/schieber/player/greedy_player/trumpf_decision.py
similarity index 96%
rename from pyschieber/player/greedy_player/trumpf_decision.py
rename to schieber/player/greedy_player/trumpf_decision.py
index 3fe4463..109846e 100644
--- a/pyschieber/player/greedy_player/trumpf_decision.py
+++ b/schieber/player/greedy_player/trumpf_decision.py
@@ -1,9 +1,9 @@
from enum import Enum
from operator import itemgetter
-from pyschieber.helpers.game_helper import *
-from pyschieber.trumpf import Trumpf
-from pyschieber.rules.count_rules import counting_factor
+from schieber.helpers.game_helper import *
+from schieber.trumpf import Trumpf
+from schieber.rules.count_rules import counting_factor
# https://www.jassverzeichnis.ch/index.php/blog/95-jass-tipps-trumpfansagen-schieber
TrumpfType = Enum('TrumpfType',
diff --git a/schieber/player/model_player.py b/schieber/player/model_player.py
new file mode 100644
index 0000000..fc7ca20
--- /dev/null
+++ b/schieber/player/model_player.py
@@ -0,0 +1,40 @@
+import logging
+
+from gym_jass.envs import SchieberEnv
+from jass_bot.rl import JassPolicy
+from schieber.player.greedy_player.greedy_player import GreedyPlayer
+from stable_baselines import PPO2
+from stable_baselines.common.vec_env import DummyVecEnv
+
+from schieber.player.challenge_player.challenge_player import ChallengePlayer
+
+from schieber.player.base_player import BasePlayer
+
+from schieber.player.external_player import ExternalPlayer
+
+logger = logging.getLogger(__name__)
+
+
+class ModelPlayer(GreedyPlayer):
+ """
+ This player can be used to evaluate the strength of a trained RL model.
+ """
+
+ def __init__(self, name='unknown', seed=None, trumps='all',
+ model_path="/Users/joelito/MEGA/Studium/Master/Informatik/Courses/Data Science/Very Deep Learning/Project/schieber/tests/benchmarks/models/stich-higher-learning-rate_env=Schieber-v0_gamma=0.89_nsteps=90_learning_rate=0.001_policy=JassPolicy_model=PPO2_time=2019-01-13_12:08:28_final.pkl"):
+ """
+ Inits the player with a trained model. This model is based on the stable_baselines framework.
+ (It has to implement the predict() function)
+ :param model_path:
+ """
+ super().__init__(name, seed, trumps)
+ env = SchieberEnv()
+ env = DummyVecEnv([lambda: env])
+ self.model = PPO2.load(load_path=model_path, env=env, policy=JassPolicy)
+
+
+def choose_card(self, state=None):
+ obs_dict = ExternalPlayer.build_observation(state, self.cards)
+ obs = SchieberEnv.observation_dict_to_onehot_matrix(obs_dict)
+ action = self.model.predict(obs)[0]
+ return self.cards[action]
diff --git a/schieber/player/random_player.py b/schieber/player/random_player.py
new file mode 100644
index 0000000..3f1f4b9
--- /dev/null
+++ b/schieber/player/random_player.py
@@ -0,0 +1,25 @@
+import random
+
+from schieber.player.base_player import BasePlayer
+from schieber.trumpf import Trumpf
+
+
+class RandomPlayer(BasePlayer):
+ def choose_trumpf(self, geschoben):
+ if self.trumps == 'all':
+ return self.move(choices=list(Trumpf))
+ elif self.trumps == 'obe_abe':
+ return self.move(choices=[Trumpf.OBE_ABE])
+
+ def choose_card(self, state=None):
+ cards = self.allowed_cards(state=state)
+ return self.move(choices=cards)
+
+ def move(self, choices):
+ allowed = False
+ while not allowed:
+ random.seed(self.seed)
+ choice = random.choice(choices)
+ allowed = yield choice
+ if allowed:
+ yield None
diff --git a/pyschieber/player/server_player/__init__.py b/schieber/player/server_player/__init__.py
similarity index 100%
rename from pyschieber/player/server_player/__init__.py
rename to schieber/player/server_player/__init__.py
diff --git a/pyschieber/player/server_player/helpers/__init__.py b/schieber/player/server_player/helpers/__init__.py
similarity index 100%
rename from pyschieber/player/server_player/helpers/__init__.py
rename to schieber/player/server_player/helpers/__init__.py
diff --git a/pyschieber/player/server_player/helpers/messages.py b/schieber/player/server_player/helpers/messages.py
similarity index 99%
rename from pyschieber/player/server_player/helpers/messages.py
rename to schieber/player/server_player/helpers/messages.py
index b2b8c51..87a4000 100644
--- a/pyschieber/player/server_player/helpers/messages.py
+++ b/schieber/player/server_player/helpers/messages.py
@@ -1,6 +1,6 @@
from enum import Enum
-from pyschieber.player.server_player.helpers.server_cards import ServerCard, Color
+from schieber.player.server_player.helpers.server_cards import ServerCard, Color
class GameType:
diff --git a/pyschieber/player/server_player/helpers/parser/__init__.py b/schieber/player/server_player/helpers/parser/__init__.py
similarity index 100%
rename from pyschieber/player/server_player/helpers/parser/__init__.py
rename to schieber/player/server_player/helpers/parser/__init__.py
diff --git a/pyschieber/player/server_player/helpers/parser/card_parser.py b/schieber/player/server_player/helpers/parser/card_parser.py
similarity index 60%
rename from pyschieber/player/server_player/helpers/parser/card_parser.py
rename to schieber/player/server_player/helpers/parser/card_parser.py
index e9317a0..b02df49 100644
--- a/pyschieber/player/server_player/helpers/parser/card_parser.py
+++ b/schieber/player/server_player/helpers/parser/card_parser.py
@@ -1,6 +1,6 @@
-from pyschieber.card import Card as PyschieberCard
-from pyschieber.player.server_player.helpers.parser.color_parser import suit_to_color, color_to_suit
-from pyschieber.player.server_player.helpers.server_cards import ServerCard
+from schieber.card import Card as PyschieberCard
+from schieber.player.server_player.helpers.parser.color_parser import suit_to_color, color_to_suit
+from schieber.player.server_player.helpers.server_cards import ServerCard
def pyscheiber_card_to_challenge_card(pyschieber_card):
diff --git a/pyschieber/player/server_player/helpers/parser/color_parser.py b/schieber/player/server_player/helpers/parser/color_parser.py
similarity index 77%
rename from pyschieber/player/server_player/helpers/parser/color_parser.py
rename to schieber/player/server_player/helpers/parser/color_parser.py
index edd8b81..a487a06 100644
--- a/pyschieber/player/server_player/helpers/parser/color_parser.py
+++ b/schieber/player/server_player/helpers/parser/color_parser.py
@@ -1,5 +1,5 @@
-from pyschieber.suit import Suit
-from pyschieber.player.server_player.helpers.server_cards import Color
+from schieber.suit import Suit
+from schieber.player.server_player.helpers.server_cards import Color
suit_to_color_dict = {Suit.ROSE: Color.HEARTS, Suit.ACORN: Color.DIAMONDS, Suit.BELL: Color.CLUBS,
Suit.SHIELD: Color.SPADES}
diff --git a/pyschieber/player/server_player/helpers/parser/game_type_parser.py b/schieber/player/server_player/helpers/parser/game_type_parser.py
similarity index 77%
rename from pyschieber/player/server_player/helpers/parser/game_type_parser.py
rename to schieber/player/server_player/helpers/parser/game_type_parser.py
index accbf87..1aa5ebd 100644
--- a/pyschieber/player/server_player/helpers/parser/game_type_parser.py
+++ b/schieber/player/server_player/helpers/parser/game_type_parser.py
@@ -1,7 +1,7 @@
-from pyschieber.player.server_player.helpers.messages import GameType
-from pyschieber.player.server_player.helpers.parser.color_parser import suit_to_color, color_to_suit
-from pyschieber.suit import Suit
-from pyschieber.trumpf import Trumpf
+from schieber.player.server_player.helpers.messages import GameType
+from schieber.player.server_player.helpers.parser.color_parser import suit_to_color, color_to_suit
+from schieber.suit import Suit
+from schieber.trumpf import Trumpf
def pyschieber_trumpf_to_game_type(pyschieber_trumpf):
diff --git a/pyschieber/player/server_player/helpers/server_cards.py b/schieber/player/server_player/helpers/server_cards.py
similarity index 100%
rename from pyschieber/player/server_player/helpers/server_cards.py
rename to schieber/player/server_player/helpers/server_cards.py
diff --git a/pyschieber/player/server_player/helpers/web_socket_handler.py b/schieber/player/server_player/helpers/web_socket_handler.py
similarity index 91%
rename from pyschieber/player/server_player/helpers/web_socket_handler.py
rename to schieber/player/server_player/helpers/web_socket_handler.py
index 1b95450..98c458a 100644
--- a/pyschieber/player/server_player/helpers/web_socket_handler.py
+++ b/schieber/player/server_player/helpers/web_socket_handler.py
@@ -3,7 +3,7 @@
import asyncio
import websockets
-from pyschieber.player.server_player.helpers import messages
+from schieber.player.server_player.helpers import messages
logger = logging.getLogger(__name__)
@@ -21,6 +21,7 @@ async def handler(self):
message = await websocket.recv()
except websockets.exceptions.ConnectionClosed:
self.started = False
+ logger.debug("Stopped, because connection is closed.")
break
logger.debug("Received message {}".format(message))
payload = json.loads(message)
diff --git a/pyschieber/player/server_player/requirements.txt b/schieber/player/server_player/requirements.txt
similarity index 100%
rename from pyschieber/player/server_player/requirements.txt
rename to schieber/player/server_player/requirements.txt
diff --git a/pyschieber/player/server_player/server_player.py b/schieber/player/server_player/server_player.py
similarity index 93%
rename from pyschieber/player/server_player/server_player.py
rename to schieber/player/server_player/server_player.py
index 3b345f4..4f18f32 100644
--- a/pyschieber/player/server_player/server_player.py
+++ b/schieber/player/server_player/server_player.py
@@ -2,17 +2,17 @@
import logging
from enum import Enum
-from pyschieber.player.server_player.helpers.parser.game_type_parser import pyschieber_trumpf_to_game_type, \
+from schieber.player.server_player.helpers.parser.game_type_parser import pyschieber_trumpf_to_game_type, \
game_type_to_pyschieber_trumpf
-from pyschieber.player.server_player.helpers import messages
-from pyschieber.player.server_player.helpers.messages import MessageType
-from pyschieber.player.server_player.helpers.parser.card_parser import pyscheiber_card_to_challenge_card, \
+from schieber.player.server_player.helpers import messages
+from schieber.player.server_player.helpers.messages import MessageType
+from schieber.player.server_player.helpers.parser.card_parser import pyscheiber_card_to_challenge_card, \
challenge_card_to_pyschieber_card
-from pyschieber.player.server_player.helpers.server_cards import ServerCard
-from pyschieber.player.server_player.helpers.web_socket_handler import WebSocketHandler
-from pyschieber.rules.stich_rules import card_allowed
-from pyschieber.rules.trumpf_rules import trumpf_allowed
+from schieber.player.server_player.helpers.server_cards import ServerCard
+from schieber.player.server_player.helpers.web_socket_handler import WebSocketHandler
+from schieber.rules.stich_rules import card_allowed
+from schieber.rules.trumpf_rules import trumpf_allowed
logger = logging.getLogger(__name__)
@@ -185,7 +185,7 @@ def handle_reject_card(self, data):
logger.warning(" ###### SERVER REJECTED CARD #######")
logger.warning("Player: {}".format(self.pyschieber_bot.name))
logger.warning("Rejected card: %s", data)
- logger.warning("Hand cards pyschieber: %s", self.pyschieber_bot.cards)
+ logger.warning("Hand cards schieber: %s", self.pyschieber_bot.cards)
logger.warning("Hand cards: %s", self.hand_cards)
logger.warning("Table cards: %s", self.table)
logger.warning("Gametype: %s", self.game_type)
@@ -269,4 +269,4 @@ def pyschieber_stich(self, stich_player_id):
self.stiche.append(stich)
def is_max_game_reached(self):
- return self.max_games >= 0 and self.max_games <= self.count_games
+ return 0 <= self.max_games <= self.count_games
diff --git a/pyschieber/rules/__init__.py b/schieber/rules/__init__.py
similarity index 100%
rename from pyschieber/rules/__init__.py
rename to schieber/rules/__init__.py
diff --git a/pyschieber/rules/count_rules.py b/schieber/rules/count_rules.py
similarity index 85%
rename from pyschieber/rules/count_rules.py
rename to schieber/rules/count_rules.py
index 27ffbfc..7f74d68 100644
--- a/pyschieber/rules/count_rules.py
+++ b/schieber/rules/count_rules.py
@@ -1,4 +1,4 @@
-from pyschieber.trumpf import Trumpf
+from schieber.trumpf import Trumpf
counting_factor = {Trumpf.ROSE: 1, Trumpf.ACORN: 1, Trumpf.BELL: 2, Trumpf.SHIELD: 2, Trumpf.OBE_ABE: 3,
Trumpf.UNDE_UFE: 3}
@@ -15,6 +15,13 @@
def count_stich(cards, trumpf, last=False):
+ """
+ Counts the points of a stich based on the rules of Jassen
+ :param cards:
+ :param trumpf:
+ :param last:
+ :return:
+ """
points = 0 if not last else 5
for card in cards:
if trumpf == Trumpf.OBE_ABE or trumpf == Trumpf.UNDE_UFE or card.suit.name == trumpf.name:
diff --git a/pyschieber/rules/stich_rules.py b/schieber/rules/stich_rules.py
similarity index 98%
rename from pyschieber/rules/stich_rules.py
rename to schieber/rules/stich_rules.py
index 30ff3a0..126efde 100644
--- a/pyschieber/rules/stich_rules.py
+++ b/schieber/rules/stich_rules.py
@@ -1,7 +1,7 @@
from functools import partial
-from pyschieber.stich import Stich
-from pyschieber.trumpf import Trumpf
+from schieber.stich import Stich
+from schieber.trumpf import Trumpf
UNDER = 11
NAELL = 9
diff --git a/schieber/rules/trumpf_rules.py b/schieber/rules/trumpf_rules.py
new file mode 100644
index 0000000..1a86794
--- /dev/null
+++ b/schieber/rules/trumpf_rules.py
@@ -0,0 +1,11 @@
+from schieber.trumpf import Trumpf
+
+
+def trumpf_allowed(chosen_trumpf, geschoben):
+ """
+ Disallows 'geschoben' when the partner already has chose 'geschoben'. All other trumpfs are always allowed.
+ :param chosen_trumpf:
+ :param geschoben:
+ :return:
+ """
+ return not (chosen_trumpf == Trumpf.SCHIEBEN and geschoben)
diff --git a/pyschieber/rules/wies_rules.py b/schieber/rules/wies_rules.py
similarity index 100%
rename from pyschieber/rules/wies_rules.py
rename to schieber/rules/wies_rules.py
diff --git a/schieber/stich.py b/schieber/stich.py
new file mode 100644
index 0000000..6577f7d
--- /dev/null
+++ b/schieber/stich.py
@@ -0,0 +1,34 @@
+from collections import namedtuple
+
+PlayedCard = namedtuple('PlayedCard', ['player', 'card'])
+Stich = namedtuple('Stich', ['player', 'played_cards', 'trumpf'])
+
+
+def played_card_dict(played_card):
+ """
+ Returns a dictionary containing:
+ - the player who played the card
+ - the played card
+ :param played_card:
+ :return:
+ """
+ return {
+ 'player_id': played_card.player.id,
+ 'card': str(played_card.card)
+ }
+
+
+def stich_dict(stich):
+ """
+ Returns a dictionary of the stich containing:
+ - the id of the player who is winner of the stich
+ - the trumpf
+ - the cards played in the stich
+ :param stich:
+ :return:
+ """
+ return {
+ 'player_id': stich.player.id,
+ 'trumpf': stich.trumpf.name,
+ 'played_cards': [played_card_dict(played_card) for played_card in stich.played_cards]
+ }
diff --git a/schieber/suit.py b/schieber/suit.py
new file mode 100644
index 0000000..b23c469
--- /dev/null
+++ b/schieber/suit.py
@@ -0,0 +1,10 @@
+from enum import Enum
+
+
+# Suit = Enum('Suit',['ROSE', 'BELL', 'ACORN', 'SHIELD'])
+
+class Suit(Enum):
+ ROSE = 0
+ BELL = 1
+ ACORN = 2
+ SHIELD = 3
diff --git a/schieber/team.py b/schieber/team.py
new file mode 100644
index 0000000..d7e57c0
--- /dev/null
+++ b/schieber/team.py
@@ -0,0 +1,30 @@
+class Team:
+ def __init__(self, players=None):
+ self.points = 0
+ self.players = players
+
+ def player_by_number(self, number):
+ """
+ Returns the player by the number in the team. The number should be either 0 or 1.
+ :param number:
+ :return:
+ """
+ for player in self.players:
+ if player.number == number:
+ return player
+ return None
+
+ def won(self, point_limit):
+ """
+ Checks if the team already won
+ :param point_limit:
+ :return: true if the points of the team are larger than the point limit and false otherwise
+ """
+ return self.points >= point_limit
+
+ def reset_points(self):
+ """
+ Resets the points to 0. This is used when single games and no tournaments are played.
+ :return:
+ """
+ self.points = 0
diff --git a/pyschieber/tournament.py b/schieber/tournament.py
similarity index 59%
rename from pyschieber/tournament.py
rename to schieber/tournament.py
index 695f8ed..0b21a93 100644
--- a/pyschieber/tournament.py
+++ b/schieber/tournament.py
@@ -1,44 +1,73 @@
import logging
-from pyschieber.game import Game
-from pyschieber.team import Team
+from schieber.game import Game
+from schieber.team import Team
logger = logging.getLogger(__name__)
class Tournament:
- def __init__(self, point_limit=1500):
+ def __init__(self, point_limit=1500, seed=None):
+ """
+ Sets the point limit and initializes the players, teams and games arrays.
+ :param point_limit:
+ """
self.point_limit = point_limit
self.players = []
self.teams = []
self.games = []
+ self.seed = seed
def check_players(self):
+ """
+ Checks if there are really 4 players in the array
+ :return:
+ """
player_numbers = []
for index, player in enumerate(self.players):
player_numbers.append(index)
assert {0, 1, 2, 3} == set(player_numbers)
def register_player(self, player):
+ """
+ Adds another player if there are still less than 4 players
+ :param player:
+ :return:
+ """
number_of_players = len(self.players)
assert number_of_players < 4
self.players.append(player)
player.id = number_of_players
def build_teams(self):
+ """
+ Builds the teams based on the players array
+ :return: the team list
+ """
self.check_players()
team_1 = Team(players=[self.players[0], self.players[2]])
team_2 = Team(players=[self.players[1], self.players[3]])
self.teams = [team_1, team_2]
+ return self.teams
- def play(self, rounds=0, use_counting_factor=True):
+ def play(self, rounds=0, use_counting_factor=False):
+ """
+ Plays a tournament until one team reaches the point_limit.
+ :param rounds:
+ :param use_counting_factor: if True: Undenufe and Obenabe are counted 3-fold and Shield and Bell are counted 2-fold
+ :return:
+ """
self.build_teams()
logger.info('Tournament starts, the point limit is {}.'.format(self.point_limit))
end = False
whole_rounds = True if rounds > 0 else False
round_counter = 0
while not end:
- game = Game(teams=self.teams, point_limit=self.point_limit, use_counting_factor=use_counting_factor)
+ if self.seed is not None:
+ # Increment seed by one so that each game is different.
+ # But still the sequence of games is the same each time
+ self.seed += 1
+ game = Game(teams=self.teams, point_limit=self.point_limit, use_counting_factor=use_counting_factor, seed=self.seed)
self.games.append(game)
logger.info('-' * 200)
logger.info('Round {} starts.'.format(len(self.games)))
@@ -54,12 +83,20 @@ def play(self, rounds=0, use_counting_factor=True):
self.reset()
def get_status(self):
+ """
+ Returns the status of the tournament
+ :return:
+ """
return {
'games': [game.get_status() for game in self.games],
'players': [player.get_dict() for player in self.players]
}
def reset(self):
+ """
+ Resets the tournament. Deletes the games array and deletes the cards of the players.
+ :return:
+ """
self.games = []
for player in self.players:
player.cards = []
diff --git a/pyschieber/trumpf.py b/schieber/trumpf.py
similarity index 91%
rename from pyschieber/trumpf.py
rename to schieber/trumpf.py
index 945710e..e22909b 100644
--- a/pyschieber/trumpf.py
+++ b/schieber/trumpf.py
@@ -1,6 +1,6 @@
from enum import Enum
-from pyschieber.suit import Suit
+from schieber.suit import Suit
Trumpf = Enum('Trumpf', ['OBE_ABE', 'UNDE_UFE'] + [str(suit.name) for suit in Suit] + ['SCHIEBEN'])
diff --git a/setup.py b/setup.py
index 995f06a..0236e4f 100644
--- a/setup.py
+++ b/setup.py
@@ -1,17 +1,15 @@
-from setuptools import setup, find_packages
+import setuptools
-with open('README.md') as f:
- readme = f.read()
-
- setup(
- name='pyschieber',
- version='1.3.0',
- description='pyschieber is a terminal application of the popular swiss card game Schieber and provides an API to the game',
- long_description=readme,
- author='Samuel Kurath',
- author_email='samuel.kurath@gmail.com',
- url='https://github.com/Murthy10/pyschieber',
- license='MIT',
- packages=find_packages(exclude=('tests', 'docs')),
- scripts=['bin/pyschieber'],
- )
+setuptools.setup(
+ name='schieber',
+ version='0.1.10',
+ description='Schieber is a terminal application of the popular Swiss card game Schieber and provides an API to the game',
+ long_description=open('README.md', "r").read(),
+ long_description_content_type="text/markdown",
+ author='Joel Niklaus',
+ author_email='me@joelniklaus.ch',
+ url='https://github.com/JoelNiklaus/schieber',
+ license=open('LICENSE', "r").read(),
+ packages=setuptools.find_packages(exclude=('tests', 'docs')),
+ scripts=['bin/schieber'],
+)
diff --git a/tests/example/__init__.py b/tests/benchmarks/__init__.py
similarity index 100%
rename from tests/example/__init__.py
rename to tests/benchmarks/__init__.py
diff --git a/tests/benchmarks/benchmark_challenge_player.py b/tests/benchmarks/benchmark_challenge_player.py
new file mode 100644
index 0000000..1f480e7
--- /dev/null
+++ b/tests/benchmarks/benchmark_challenge_player.py
@@ -0,0 +1,38 @@
+import pytest
+
+from schieber.player.challenge_player.challenge_player import ChallengePlayer
+from schieber.player.greedy_player.greedy_player import GreedyPlayer
+from schieber.player.random_player import RandomPlayer
+from tests.benchmarks.statistical_helper import run_statistics
+
+
+@pytest.mark.statistical
+def test_against_random():
+ players = [ChallengePlayer(name='ChallengeActor'), RandomPlayer(name='RandomOpponent1'),
+ ChallengePlayer(name='ChallengePartner'), RandomPlayer(name='RandomOpponent2')]
+
+ run_statistics(players=players)
+
+
+@pytest.mark.statistical
+def test_with_and_against_random():
+ players = [ChallengePlayer(name='ChallengeActor'), RandomPlayer(name='RandomOpponent1'),
+ RandomPlayer(name='RandomPartner'), RandomPlayer(name='RandomOpponent2')]
+
+ run_statistics(players=players)
+
+
+@pytest.mark.statistical
+def test_against_greedy():
+ players = [ChallengePlayer(name='ChallengeActor'), GreedyPlayer(name='GreedyOpponent1'),
+ ChallengePlayer(name='ChallengePartner'), GreedyPlayer(name='GreedyOpponent2')]
+
+ run_statistics(players=players)
+
+
+@pytest.mark.statistical
+def test_with_and_against_greedy():
+ players = [ChallengePlayer(name='ChallengeActor'), GreedyPlayer(name='GreedyOpponent1'),
+ GreedyPlayer(name='GreedyPartner'), GreedyPlayer(name='GreedyOpponent2')]
+
+ run_statistics(players=players)
diff --git a/tests/benchmarks/benchmark_greedy_player.py b/tests/benchmarks/benchmark_greedy_player.py
new file mode 100644
index 0000000..091bf15
--- /dev/null
+++ b/tests/benchmarks/benchmark_greedy_player.py
@@ -0,0 +1,19 @@
+import pytest
+
+from schieber.player.greedy_player.greedy_player import GreedyPlayer
+from schieber.player.random_player import RandomPlayer
+from tests.benchmarks.statistical_helper import run_statistics
+
+
+@pytest.mark.statistical
+def test_against_random():
+ players = [GreedyPlayer(name='GreedyActor'), RandomPlayer(name='RandomOpponent1'),
+ GreedyPlayer(name='GreedyPartner'), RandomPlayer(name='RandomOpponent2')]
+ run_statistics(players=players)
+
+
+@pytest.mark.statistical
+def test_with_and_against_random():
+ players = [GreedyPlayer(name='GreedyActor'), RandomPlayer(name='RandomOpponent1'),
+ RandomPlayer(name='RandomPartner'), RandomPlayer(name='RandomOpponent2')]
+ run_statistics(players=players)
diff --git a/tests/benchmarks/benchmark_model_player.py b/tests/benchmarks/benchmark_model_player.py
new file mode 100644
index 0000000..c818cfd
--- /dev/null
+++ b/tests/benchmarks/benchmark_model_player.py
@@ -0,0 +1,53 @@
+import pytest
+from schieber.player.challenge_player.challenge_player import ChallengePlayer
+
+from schieber.player.greedy_player.greedy_player import GreedyPlayer
+from schieber.player.model_player import ModelPlayer
+from schieber.player.random_player import RandomPlayer
+from tests.benchmarks.statistical_helper import run_statistics
+
+
+@pytest.mark.statistical
+def test_against_random():
+ players = [ModelPlayer(name='ModelActor'), RandomPlayer(name='RandomOpponent1'),
+ ModelPlayer(name='ModelPartner'), RandomPlayer(name='RandomOpponent2')]
+ run_statistics(players=players)
+
+
+@pytest.mark.statistical
+def test_with_and_against_random():
+ players = [ModelPlayer(name='ModelActor'), RandomPlayer(name='RandomOpponent1'),
+ RandomPlayer(name='RandomPartner'), RandomPlayer(name='RandomOpponent2')]
+ run_statistics(players=players)
+
+
+@pytest.mark.statistical
+def test_against_greedy():
+ players = [ModelPlayer(name='ModelActor'), GreedyPlayer(name='GreedyOpponent1'),
+ ModelPlayer(name='ModelPartner'), GreedyPlayer(name='GreedyOpponent2')]
+
+ run_statistics(players=players)
+
+
+@pytest.mark.statistical
+def test_with_and_against_greedy():
+ players = [ModelPlayer(name='ModelActor'), GreedyPlayer(name='GreedyOpponent1'),
+ GreedyPlayer(name='GreedyPartner'), GreedyPlayer(name='GreedyOpponent2')]
+
+ run_statistics(players=players)
+
+
+@pytest.mark.statistical
+def test_against_challenge():
+ players = [ModelPlayer(name='ModelActor'), ChallengePlayer(name='ChallengeOpponent1'),
+ ModelPlayer(name='ModelPartner'), ChallengePlayer(name='ChallengeOpponent2')]
+
+ run_statistics(players=players)
+
+
+@pytest.mark.statistical
+def test_with_and_against_challenge():
+ players = [ModelPlayer(name='ModelActor'), ChallengePlayer(name='ChallengeOpponent1'),
+ ChallengePlayer(name='ChallengePartner'), ChallengePlayer(name='ChallengeOpponent2')]
+
+ run_statistics(players=players)
diff --git a/tests/benchmarks/models/stich-higher-learning-rate_env=Schieber-v0_gamma=0.89_nsteps=90_learning_rate=0.001_policy=JassPolicy_model=PPO2_time=2019-01-13_12:08:28_final.pkl b/tests/benchmarks/models/stich-higher-learning-rate_env=Schieber-v0_gamma=0.89_nsteps=90_learning_rate=0.001_policy=JassPolicy_model=PPO2_time=2019-01-13_12:08:28_final.pkl
new file mode 100644
index 0000000..8ab5cb1
Binary files /dev/null and b/tests/benchmarks/models/stich-higher-learning-rate_env=Schieber-v0_gamma=0.89_nsteps=90_learning_rate=0.001_policy=JassPolicy_model=PPO2_time=2019-01-13_12:08:28_final.pkl differ
diff --git a/tests/example/statistical_helper.py b/tests/benchmarks/statistical_helper.py
similarity index 73%
rename from tests/example/statistical_helper.py
rename to tests/benchmarks/statistical_helper.py
index fddbd8e..454c274 100644
--- a/tests/example/statistical_helper.py
+++ b/tests/benchmarks/statistical_helper.py
@@ -1,13 +1,10 @@
from timeit import default_timer as timer
-from pyschieber.tournament import Tournament
+from schieber.tournament import Tournament
-def run_statistics(players):
- point_limit = 1000
- number_of_tournaments = 1000
-
- tournament = Tournament(point_limit=point_limit)
+def run_statistics(players, number_of_tournaments=10, point_limit=1000):
+ tournament = Tournament(point_limit=point_limit, seed=42)
[tournament.register_player(player=player) for player in players]
team_1_won = 0
@@ -25,7 +22,7 @@ def run_statistics(players):
end = timer()
print("\nTo run {0} tournaments it took {1:.2f} seconds.".format(number_of_tournaments, end - start))
- difference = abs(team_1_won - team_2_won)
+ difference = team_1_won - team_2_won
print("Difference: ", difference)
print("Team 1: ", team_1_won)
print("Team 2: ", team_2_won)
diff --git a/tests/helpers/test_game_helper.py b/tests/helpers/test_game_helper.py
index 6c21559..b9126be 100644
--- a/tests/helpers/test_game_helper.py
+++ b/tests/helpers/test_game_helper.py
@@ -1,9 +1,9 @@
-from pyschieber.suit import Suit
+from schieber.suit import Suit
-from pyschieber.card import Card
-from pyschieber.deck import Deck
+from schieber.card import Card
+from schieber.deck import Deck
-from pyschieber.helpers.game_helper import split_card_values_by_suit, split_cards_by_suit
+from schieber.helpers.game_helper import split_card_values_by_suit, split_cards_by_suit
def test_split_card_values_by_suit():
diff --git a/tests/player/challenge_player/strategy/mode/test_bottom_up_mode.py b/tests/player/challenge_player/strategy/mode/test_bottom_up_mode.py
index 3fe10ed..a7c7d76 100644
--- a/tests/player/challenge_player/strategy/mode/test_bottom_up_mode.py
+++ b/tests/player/challenge_player/strategy/mode/test_bottom_up_mode.py
@@ -1,8 +1,8 @@
import pytest
-from pyschieber.card import Card
-from pyschieber.suit import Suit
-from pyschieber.player.challenge_player.strategy.mode.bottom_up_mode import BottomUpMode
+from schieber.card import Card
+from schieber.suit import Suit
+from schieber.player.challenge_player.strategy.mode.bottom_up_mode import BottomUpMode
@pytest.fixture
diff --git a/tests/player/challenge_player/strategy/mode/test_mode.py b/tests/player/challenge_player/strategy/mode/test_mode.py
index 0d4f807..5fd010f 100644
--- a/tests/player/challenge_player/strategy/mode/test_mode.py
+++ b/tests/player/challenge_player/strategy/mode/test_mode.py
@@ -1,8 +1,8 @@
import pytest
-from pyschieber.card import Card
-from pyschieber.suit import Suit
-from pyschieber.player.challenge_player.strategy.mode.mode import Mode
+from schieber.card import Card
+from schieber.suit import Suit
+from schieber.player.challenge_player.strategy.mode.mode import Mode
@pytest.fixture
diff --git a/tests/player/challenge_player/strategy/mode/test_top_down_mode.py b/tests/player/challenge_player/strategy/mode/test_top_down_mode.py
index 8748e16..e52159e 100644
--- a/tests/player/challenge_player/strategy/mode/test_top_down_mode.py
+++ b/tests/player/challenge_player/strategy/mode/test_top_down_mode.py
@@ -1,8 +1,8 @@
import pytest
-from pyschieber.card import Card
-from pyschieber.suit import Suit
-from pyschieber.player.challenge_player.strategy.mode.top_down_mode import TopDownMode
+from schieber.card import Card
+from schieber.suit import Suit
+from schieber.player.challenge_player.strategy.mode.top_down_mode import TopDownMode
@pytest.fixture
diff --git a/tests/player/challenge_player/strategy/mode/test_trumpf_color_mode.py b/tests/player/challenge_player/strategy/mode/test_trumpf_color_mode.py
index e9f9f27..eae8981 100644
--- a/tests/player/challenge_player/strategy/mode/test_trumpf_color_mode.py
+++ b/tests/player/challenge_player/strategy/mode/test_trumpf_color_mode.py
@@ -1,8 +1,8 @@
import pytest
-from pyschieber.card import Card
-from pyschieber.suit import Suit
-from pyschieber.player.challenge_player.strategy.mode.trumpf_color_mode import TrumpfColorMode
+from schieber.card import Card
+from schieber.suit import Suit
+from schieber.player.challenge_player.strategy.mode.trumpf_color_mode import TrumpfColorMode
@pytest.mark.parametrize("suit, cards, score", [
diff --git a/tests/player/challenge_player/strategy/test_jass_strategy.py b/tests/player/challenge_player/strategy/test_jass_strategy.py
index b16a8ee..5070636 100644
--- a/tests/player/challenge_player/strategy/test_jass_strategy.py
+++ b/tests/player/challenge_player/strategy/test_jass_strategy.py
@@ -1,10 +1,10 @@
import pytest
-from pyschieber.card import Card
-from pyschieber.suit import Suit
-from pyschieber.trumpf import Trumpf
-from pyschieber.player.challenge_player.strategy.jass_strategy import JassStrategy
-from pyschieber.player.challenge_player.challenge_player import ChallengePlayer
+from schieber.card import Card
+from schieber.suit import Suit
+from schieber.trumpf import Trumpf
+from schieber.player.challenge_player.strategy.jass_strategy import JassStrategy
+from schieber.player.challenge_player.challenge_player import ChallengePlayer
@pytest.fixture
diff --git a/tests/player/challenge_player/test_challenge_player.py b/tests/player/challenge_player/test_challenge_player.py
deleted file mode 100644
index bbfc9d0..0000000
--- a/tests/player/challenge_player/test_challenge_player.py
+++ /dev/null
@@ -1,22 +0,0 @@
-import pytest
-
-from pyschieber.player.challenge_player.challenge_player import ChallengePlayer
-from pyschieber.player.greedy_player.greedy_player import GreedyPlayer
-from pyschieber.player.random_player import RandomPlayer
-from tests.example.statistical_helper import run_statistics
-
-
-@pytest.mark.statistical
-def test_greedy():
- players = [GreedyPlayer(name='Greedy1'), RandomPlayer(name='Track1'), GreedyPlayer(name='Greedy2'),
- RandomPlayer(name='Track2')]
-
- run_statistics(players=players)
-
-
-@pytest.mark.statistical
-def test_challenge():
- players = [ChallengePlayer(name='Trick1'), GreedyPlayer(name='Greedy1'), ChallengePlayer(name='Trick2'),
- GreedyPlayer(name='Greedy2')]
-
- run_statistics(players=players)
diff --git a/tests/player/greedy_player/test_example_trumpf.py b/tests/player/greedy_player/test_example_trumpf.py
index 5bceb51..77aae16 100644
--- a/tests/player/greedy_player/test_example_trumpf.py
+++ b/tests/player/greedy_player/test_example_trumpf.py
@@ -1,9 +1,9 @@
import pytest
-from pyschieber.card import Card
-from pyschieber.player.greedy_player.trumpf_decision import choose_trumpf, TrumpfType
-from pyschieber.suit import Suit
-from pyschieber.trumpf import Trumpf
+from schieber.card import Card
+from schieber.player.greedy_player.trumpf_decision import choose_trumpf, TrumpfType
+from schieber.suit import Suit
+from schieber.trumpf import Trumpf
@pytest.mark.parametrize("cards, trumpf, trumpf_type", [
diff --git a/tests/player/greedy_player/test_greedy_player.py b/tests/player/greedy_player/test_greedy_player.py
deleted file mode 100644
index dbd7799..0000000
--- a/tests/player/greedy_player/test_greedy_player.py
+++ /dev/null
@@ -1,12 +0,0 @@
-import pytest
-
-from pyschieber.player.greedy_player.greedy_player import GreedyPlayer
-from pyschieber.player.random_player import RandomPlayer
-from tests.example.statistical_helper import run_statistics
-
-
-@pytest.mark.statistical
-def test_greedy():
- players = [GreedyPlayer(name='Greedy1'), RandomPlayer(name='Track'), GreedyPlayer(name='Greedy2'),
- RandomPlayer(name='Track')]
- run_statistics(players=players)
diff --git a/tests/player/test_base_player.py b/tests/player/test_base_player.py
index 33380a1..e714275 100644
--- a/tests/player/test_base_player.py
+++ b/tests/player/test_base_player.py
@@ -1,4 +1,4 @@
-from pyschieber.player.base_player import BasePlayer
+from schieber.player.base_player import BasePlayer
from itertools import count
diff --git a/tests/player/test_external_player.py b/tests/player/test_external_player.py
new file mode 100644
index 0000000..1df5eaf
--- /dev/null
+++ b/tests/player/test_external_player.py
@@ -0,0 +1,66 @@
+import logging
+import sys
+import time
+from threading import Thread
+
+import pytest
+from timeit import default_timer as timer
+from math import sqrt, floor
+
+from schieber.suit import Suit
+
+from schieber.card import Card
+
+from schieber.game import Game
+
+from schieber.team import Team
+
+from schieber.player.external_player import ExternalPlayer
+
+from schieber.player.random_player import RandomPlayer
+from schieber.tournament import Tournament
+
+
+def test_control():
+ logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, format='%(asctime)s %(levelname)-8s %(message)s')
+
+ players = [RandomPlayer(name='Tick', ), RandomPlayer(name='Trick'),
+ RandomPlayer(name='Track'), ExternalPlayer(name='Dagobert')]
+
+ team_1 = Team(players=[players[0], players[2]])
+ team_2 = Team(players=[players[1], players[3]])
+ teams = [team_1, team_2]
+ game = Game(teams, point_limit=1000, use_counting_factor=False, seed=1)
+
+ thread = Thread(target=game.play_endless)
+ thread.start()
+
+ player = players[3]
+ action = Card(Suit.ROSE, 9)
+
+ print("Test starts")
+
+ assert not game.stop_playing
+
+ player.get_observation()
+ assert player.before_first_stich()
+ print(len(player.cards))
+ player.set_action(action)
+
+ for i in range(7):
+ print(f"Round {i}")
+ player.get_observation()
+ print(len(player.cards))
+ assert not player.before_first_stich()
+ player.set_action(action)
+ print(len(player.cards))
+
+ print(len(player.cards))
+ assert player.at_last_stich()
+
+ game.endless_play_control.acquire()
+ game.stop_playing = True
+ game.endless_play_control.notify_all()
+ game.endless_play_control.release()
+
+ assert game.stop_playing
diff --git a/tests/player/test_random_player.py b/tests/player/test_random_player.py
index 46bf618..76a74a5 100644
--- a/tests/player/test_random_player.py
+++ b/tests/player/test_random_player.py
@@ -2,20 +2,24 @@
from timeit import default_timer as timer
from math import sqrt, floor
-from pyschieber.player.random_player import RandomPlayer
-from pyschieber.tournament import Tournament
+from schieber.player.random_player import RandomPlayer
+from schieber.tournament import Tournament
@pytest.mark.statistical
def test_is_random():
+ """
+ This test may fail sometimes if the number of tournaments is too low. But for performance reasons, this number is set low.
+ :return:
+ """
point_limit = 1000
- number_of_tournaments = 1000
+ number_of_tournaments = 10
mean = number_of_tournaments * 0.5 # assume that a RandomPlayer has a 50% chance to win
variance = mean * (1 - 0.5)
standard_deviation = int(floor(sqrt(variance)))
random_players = [RandomPlayer(name=i) for i in range(4)]
- tournament = Tournament(point_limit=point_limit)
+ tournament = Tournament(point_limit=point_limit, seed=1)
[tournament.register_player(player=player) for player in random_players]
team_1_won = 0
@@ -37,4 +41,4 @@ def test_is_random():
print("Difference: ", difference)
print("Team 1: ", team_1_won)
print("Team 2: ", team_2_won)
- assert difference in range(0, 2 * standard_deviation)
+ assert difference in range(0, 4 * standard_deviation) # if a harder constraint is required replace 4 by 2 or 1
diff --git a/tests/rules/test_count_rules.py b/tests/rules/test_count_rules.py
index ca49bfa..c2b096c 100644
--- a/tests/rules/test_count_rules.py
+++ b/tests/rules/test_count_rules.py
@@ -1,8 +1,8 @@
import pytest
-from pyschieber.deck import Deck
-from pyschieber.trumpf import Trumpf
-from pyschieber.rules.count_rules import count_stich
+from schieber.deck import Deck
+from schieber.trumpf import Trumpf
+from schieber.rules.count_rules import count_stich
@pytest.mark.parametrize("trumpf", list(Trumpf)[:6])
diff --git a/tests/rules/test_stich_rules.py b/tests/rules/test_stich_rules.py
index b310fe4..1ae6d95 100644
--- a/tests/rules/test_stich_rules.py
+++ b/tests/rules/test_stich_rules.py
@@ -1,12 +1,12 @@
import pytest
-from pyschieber.rules.stich_rules import stich_rules, card_allowed, allowed_cards, is_trumpf_under, does_under_trumpf, \
+from schieber.rules.stich_rules import stich_rules, card_allowed, allowed_cards, is_trumpf_under, does_under_trumpf, \
is_chosen_card_best_trumpf
-from pyschieber.trumpf import Trumpf
-from pyschieber.card import Card
-from pyschieber.player.random_player import RandomPlayer
-from pyschieber.stich import PlayedCard
-from pyschieber.suit import Suit
+from schieber.trumpf import Trumpf
+from schieber.card import Card
+from schieber.player.random_player import RandomPlayer
+from schieber.stich import PlayedCard
+from schieber.suit import Suit
@pytest.fixture(scope="module", autouse=True)
diff --git a/tests/rules/test_trumpf_rules.py b/tests/rules/test_trumpf_rules.py
index 02b6c8d..3592d66 100644
--- a/tests/rules/test_trumpf_rules.py
+++ b/tests/rules/test_trumpf_rules.py
@@ -1,7 +1,7 @@
import pytest
-from pyschieber.trumpf import Trumpf
-from pyschieber.rules.trumpf_rules import trumpf_allowed
+from schieber.trumpf import Trumpf
+from schieber.rules.trumpf_rules import trumpf_allowed
@pytest.mark.parametrize("trumpf, geschoben, result", [
diff --git a/tests/test_card.py b/tests/test_card.py
index f93e3a0..910043e 100644
--- a/tests/test_card.py
+++ b/tests/test_card.py
@@ -1,8 +1,15 @@
import pytest
-from pyschieber.suit import Suit
+from schieber.suit import Suit
-from pyschieber.card import Card, from_string_to_card
+from schieber.card import Card, from_string_to_card, from_card_to_tuple, from_tuple_to_card, from_string_to_tuple, \
+ from_tuple_to_string, from_index_to_card, from_card_to_index, from_index_to_string, from_string_to_index, \
+ from_card_to_onehot, from_onehot_to_card, from_onehot_to_string, from_string_to_onehot
+
+
+@pytest.fixture(scope='module')
+def card():
+ return Card(suit=Suit.ROSE, value=6)
@pytest.fixture(scope='module')
@@ -11,8 +18,18 @@ def card_string():
@pytest.fixture(scope='module')
-def card():
- return Card(suit=Suit.ROSE, value=6)
+def card_tuple():
+ return 0, 0
+
+
+@pytest.fixture(scope='module')
+def card_index():
+ return 1
+
+
+@pytest.fixture(scope='module')
+def card_onehot():
+ return [1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
def test_card_str(card, card_string):
@@ -23,6 +40,54 @@ def test_from_string_to_card(card, card_string):
assert card == from_string_to_card(card_string)
+def test_from_card_to_tuple(card, card_tuple):
+ assert card_tuple == from_card_to_tuple(card)
+
+
+def test_from_tuple_to_card(card, card_tuple):
+ assert card == from_tuple_to_card(card_tuple)
+
+
+def test_from_string_to_tuple(card_tuple, card_string):
+ assert card_tuple == from_string_to_tuple(card_string)
+
+
+def test_from_tuple_to_string(card_tuple, card_string):
+ assert card_string == from_tuple_to_string(card_tuple)
+
+
+def test_from_card_to_index(card, card_index):
+ assert card_index == from_card_to_index(card)
+
+
+def test_from_index_to_card(card, card_index):
+ assert card == from_index_to_card(card_index)
+
+
+def test_from_index_to_string(card_string, card_index):
+ assert card_string == from_index_to_string(card_index)
+
+
+def test_from_string_to_index(card_string, card_index):
+ assert card_index == from_string_to_index(card_string)
+
+
+def test_from_card_to_onehot(card, card_onehot):
+ assert card_onehot == from_card_to_onehot(card)
+
+
+def test_from_onehot_to_card(card, card_onehot):
+ assert card == from_onehot_to_card(card_onehot)
+
+
+def test_from_onehot_to_string(card_string, card_onehot):
+ assert card_string == from_onehot_to_string(card_onehot)
+
+
+def test_from_string_to_onehot(card_string, card_onehot):
+ assert card_onehot == from_string_to_onehot(card_string)
+
+
def test_from_string_not_card(card):
rose_7 = Card(suit=Suit.ROSE, value=7)
assert card != from_string_to_card(str(rose_7))
diff --git a/tests/test_dealer.py b/tests/test_dealer.py
index f6a6d2a..9db946d 100644
--- a/tests/test_dealer.py
+++ b/tests/test_dealer.py
@@ -1,8 +1,8 @@
import pytest
-from pyschieber.player.random_player import RandomPlayer
+from schieber.player.random_player import RandomPlayer
-from pyschieber.dealer import Dealer
+from schieber.dealer import Dealer
@pytest.fixture(scope="function")
diff --git a/tests/test_deck.py b/tests/test_deck.py
index 5005270..ddea579 100644
--- a/tests/test_deck.py
+++ b/tests/test_deck.py
@@ -1,5 +1,5 @@
-from pyschieber.card import Card
-from pyschieber.deck import Deck
+from schieber.card import Card
+from schieber.deck import Deck
def test_deck_count():
diff --git a/tests/test_game.py b/tests/test_game.py
index 4c72691..23ba0fd 100644
--- a/tests/test_game.py
+++ b/tests/test_game.py
@@ -1,14 +1,14 @@
import pytest
-from pyschieber.rules.count_rules import counting_factor
+from schieber.rules.count_rules import counting_factor
-from pyschieber.deck import Deck
+from schieber.deck import Deck
-from pyschieber.trumpf import Trumpf
+from schieber.trumpf import Trumpf
-from pyschieber.player.random_player import RandomPlayer
+from schieber.player.random_player import RandomPlayer
-from pyschieber.game import Game, get_player_index
-from pyschieber.team import Team
+from schieber.game import Game, get_player_index
+from schieber.team import Team
@pytest.mark.parametrize("start_key, last_key", [
@@ -39,6 +39,20 @@ def test_game():
assert len(player.cards) == 0
+def test_reset_points():
+ random_players = [RandomPlayer(name=i) for i in range(4)]
+ team_1 = Team(players=[random_players[0], random_players[1]])
+ team_2 = Team(players=[random_players[1], random_players[2]])
+ teams = [team_1, team_2]
+ game = Game(teams=teams, point_limit=1500)
+ game.play()
+
+ game.reset_points()
+
+ for team in game.teams:
+ assert team.points == 0
+
+
@pytest.mark.parametrize("start_key, next_key", [
(0, 1),
(1, 2),
diff --git a/tests/test_team.py b/tests/test_team.py
index a6bf2de..6ee9ca4 100644
--- a/tests/test_team.py
+++ b/tests/test_team.py
@@ -1,7 +1,7 @@
import pytest
-from pyschieber.player.random_player import RandomPlayer
-from pyschieber.team import Team
+from schieber.player.random_player import RandomPlayer
+from schieber.team import Team
@pytest.mark.parametrize("points, point_limit, won", [
diff --git a/tests/test_tournament.py b/tests/test_tournament.py
index 44b520f..d896d92 100644
--- a/tests/test_tournament.py
+++ b/tests/test_tournament.py
@@ -1,6 +1,6 @@
import pytest
-from pyschieber.player.random_player import RandomPlayer
-from pyschieber.tournament import Tournament
+from schieber.player.random_player import RandomPlayer
+from schieber.tournament import Tournament
@pytest.fixture(scope='module')
diff --git a/tests/test_trumpf.py b/tests/test_trumpf.py
index 5a6ff8c..ff84519 100644
--- a/tests/test_trumpf.py
+++ b/tests/test_trumpf.py
@@ -1,4 +1,4 @@
-from pyschieber.trumpf import Trumpf
+from schieber.trumpf import Trumpf
def test_trumpf_count():