Commit 0ab35cc2 authored by Wolfgang Rohdewald's avatar Wolfgang Rohdewald
Browse files

new Classes Wind, East etc.

parent ed9bf140
......@@ -33,6 +33,7 @@ src/client.py
src/intelligence.py
src/altint.py
src/common.py
src/wind.py
src/compat.py
src/rand.py
src/config.py
......
......@@ -35,7 +35,8 @@ from message import Message
from util import kprint, stack, uniqueList
from log import logDebug, logException, m18n, m18nc
from common import WINDS, LIGHTSOURCES, Internal, Debug, isAlive, unicode
from common import LIGHTSOURCES, Internal, Debug, isAlive, unicode
from wind import Wind, East
ROUNDWINDCOLOR = QColor(235, 235, 173)
......@@ -57,8 +58,9 @@ class PlayerWind(QGraphicsEllipseItem):
def __init__(self, name, tileset, roundsFinished=0, parent=None):
"""generate new wind tile"""
assert isinstance(name, Wind), 'PlayerWind expects Wind, not {}'.format(type(name))
if not len(WINDPIXMAPS):
WINDPIXMAPS[('E', False)] = None # avoid recursion
WINDPIXMAPS[(East, False)] = None # avoid recursion
self.genWINDPIXMAPS()
QGraphicsEllipseItem.__init__(self)
if parent:
......@@ -75,7 +77,7 @@ class PlayerWind(QGraphicsEllipseItem):
def genWINDPIXMAPS():
"""prepare wind tiles"""
tileset = Tileset(Internal.Preferences.windTilesetName)
for wind in WINDS:
for wind in Wind.all4:
for prevailing in False, True:
pwind = PlayerWind(wind, tileset, prevailing)
pMap = QPixmap(40, 40)
......@@ -108,14 +110,15 @@ class PlayerWind(QGraphicsEllipseItem):
def setWind(self, name, roundsFinished):
"""change the wind"""
assert isinstance(name, Wind) and name.svgName, 'name {} must be a real Wind but is {}'.format(
name, type(name))
self.name = name
if isinstance(roundsFinished, bool):
self.prevailing = roundsFinished
else:
self.prevailing = name == WINDS[roundsFinished % 4]
self.prevailing = name == Wind.all4[roundsFinished % 4]
self.setBrush(ROUNDWINDCOLOR if self.prevailing else QColor('white'))
windtilenr = {'N': 1, 'S': 2, 'E': 3, 'W': 4}
self.face.setElementId('WIND_%d' % windtilenr[name])
self.face.setElementId(name.svgName)
class WindLabel(QLabel):
......@@ -138,7 +141,7 @@ class WindLabel(QLabel):
QLabel.__init__(self, parent)
self.__wind = None
if wind is None:
wind = 'E'
wind = East
self.__roundsFinished = roundsFinished
self.wind = wind
......@@ -158,7 +161,7 @@ class WindLabel(QLabel):
"""update pixmaps"""
PlayerWind.genWINDPIXMAPS()
self.setPixmap(WINDPIXMAPS[(self.__wind,
self.__wind == WINDS[min(self.__roundsFinished, 3)])])
self.__wind == Wind.all4[min(self.__roundsFinished, 3)])])
class Board(QGraphicsRectItem):
......@@ -739,7 +742,7 @@ class SelectorBoard(CourtBoard):
Tile.wind: (3, 0, Tile.winds), Tile.bamboo: (1, 0, Tile.numbers), Tile.stone: (2, 0, Tile.numbers),
Tile.character: (0, 0, Tile.numbers)}
row, baseColumn, order = offsets[uiTile.tile.lowerGroup]
column = baseColumn + order.index(uiTile.tile.value)
column = baseColumn + order.index(uiTile.tile.char)
uiTile.dark = False
uiTile.setBoard(self, column, row)
......
......@@ -57,7 +57,6 @@ else:
interpreterName = 'python2'
xrange = xrange
WINDS = u'ESWN'
LIGHTSOURCES = [u'NE', u'NW', u'SW', u'SE']
ENGLISHDICT = {}
......
......@@ -30,8 +30,9 @@ from twisted.internet.defer import succeed
from util import gitHead
from rand import CountingRandom
from log import logError, logWarning, logException, logDebug, m18n
from common import WINDS, Internal, IntDict, Debug, Options, unicodeString, unicode
from common import Internal, IntDict, Debug, Options, unicodeString, unicode
from common import isPython3
from wind import Wind, East
from query import Query
from rule import Ruleset
from tile import Tile, elements
......@@ -95,16 +96,16 @@ class HandId(object):
self.roundsFinished = 100
return
handId = parts[min(stringIdx, len(parts) - 1)]
if handId[0] not in WINDS:
logException('--game=%s with / must specify the round wind'
% string)
if handId[0].lower() not in 'eswn':
logException('--game=%s must specify the round wind' % string)
handWind = Wind(handId[0])
ruleset = self.game.ruleset
self.roundsFinished = WINDS.index(handId[0])
self.roundsFinished = handWind.__index__()
if self.roundsFinished > ruleset.minRounds:
logWarning(
u'Ruleset %s has %d minimum rounds but you want round %d(%s)'
% (ruleset.name, ruleset.minRounds, self.roundsFinished + 1,
handId[0]))
handWind))
self.roundsFinished = ruleset.minRounds
return
self.rotated = int(handId[1]) - 1
......@@ -154,7 +155,7 @@ class HandId(object):
num = (num - 1) // 26
if not charId:
charId = ' ' # align to the most common case
wind = (WINDS + 'X')[self.roundsFinished]
wind = Wind.all4[self.roundsFinished % 4]
if withSeed:
seed = str(self.seed)
else:
......@@ -208,6 +209,9 @@ class Game(object):
"""
# pylint: disable=too-many-statements
assert self.__class__ != Game, 'Do not directly instantiate Game'
for wind, name in names:
assert isinstance(wind, Wind), 'Game.__init__ expects Wind objects'
assert isinstance(name, (str, unicode)), 'Game.__init__: name must be string and not {}'.format(type(name))
self.players = Players()
# if we fail later on in init, at least we can still close the program
self.myself = None
......@@ -329,7 +333,7 @@ class Game(object):
@property
def roundWind(self):
"""the round wind for Hand"""
return 'eswn'[self.roundsFinished % 4]
return Wind.all[self.roundsFinished % 4]
@winner.setter
def winner(self, value):
......@@ -416,17 +420,16 @@ class Game(object):
"""assign random seats to the players and assign winds"""
self.players.sort(key=lambda x: x.name)
self.randomGenerator.shuffle(self.players)
for player, wind in zip(self.players, WINDS):
for player, wind in zip(self.players, Wind.all4):
player.wind = wind
def __exchangeSeats(self):
"""execute seat exchanges according to the rules"""
winds = self.shiftRules.split(',')[(self.roundsFinished - 1) % 4]
players = list(self.players[x] for x in winds)
winds = list(x for x in self.shiftRules.split(',')[(self.roundsFinished - 1) % 4])
players = list(self.players[Wind(x)] for x in winds)
pairs = list(players[x:x + 2] for x in range(0, len(winds), 2))
for playerA, playerB in self._mustExchangeSeats(pairs):
playerA.wind, playerB.wind = playerB.wind, playerA.wind
self.sortPlayers()
def _mustExchangeSeats(self, pairs):
"""filter: which player pairs should really swap places?"""
......@@ -435,10 +438,8 @@ class Game(object):
def sortPlayers(self):
"""sort by wind order. Place ourself at bottom (idx=0)"""
self.players.sort(key=lambda x: WINDS.index(x.wind))
self.activePlayer = self.players[u'E'] # pylint: disable=invalid-sequence-index
# TODO: new class Wind(str) with len==1 and __index__ 0..3 for ESWN
# that will make pylint happier
self.players.sort(key=lambda x: x.wind)
self.activePlayer = self.players[East]
if Internal.scene:
if self.belongsToHumanPlayer():
while self.players[0] != self.myself:
......@@ -542,7 +543,7 @@ class Game(object):
"VALUES(%d,%d,?,?,%d,'%s',%d,'%s','%s',%d,%d,%d,%d,%d)" %
(self.gameid, self.handctr, player.nameid,
scoretime, int(player == self.__winner),
WINDS[self.roundsFinished % 4], player.wind,
self.roundWind.char, player.wind,
player.handTotal, player.payment, player.balance,
self.rotated, self.notRotated),
(player.hand.string, manualrules))
......@@ -658,8 +659,8 @@ class Game(object):
if not qScoreRecords:
# this should normally not happen
qScoreRecords = list([
tuple([qGameRecord[x], WINDS[x], 0, False, 'E'])
for x in range(4)])
tuple([qGameRecord[wind], wind.char, 0, False, East.char])
for wind in Wind.all4])
if len(qScoreRecords) != 4:
logError(u'game %d inconsistent: There should be exactly '
'4 score records for the last hand' % gameid)
......@@ -673,7 +674,7 @@ class Game(object):
logError(u'game %d inconsistent: All score records for the same '
'hand must have the same prevailing wind' % gameid)
players = list(tuple([x[1], Game.__getName(x[0])])
players = list(tuple([Wind(x[1]), Game.__getName(x[0])])
for x in qScoreRecords)
# create the game instance.
......@@ -692,7 +693,7 @@ class Game(object):
player.getsPayment(record[2])
if record[3]:
game.winner = player
game.roundsFinished = WINDS.index(qScoreRecords[0][4]) # prevailing wind
game.roundsFinished = Wind(qScoreRecords[0][4]).__index__()
game.handctr += 1
game.notRotated += 1
game.maybeRotateWinds()
......@@ -729,7 +730,7 @@ class Game(object):
(self.handId, winner, guilty))
guilty.hand.usedRules.append((payAction, None))
score = winner.handTotal
score = score * 6 if winner.wind == 'E' else score * 4
score = score * 6 if winner.wind == East else score * 4
guilty.getsPayment(-score)
winner.getsPayment(score)
return
......@@ -742,7 +743,7 @@ class Game(object):
self.debug(' %s' % (line))
for player2 in self.players:
if id(player1) != id(player2):
if player1.wind == 'E' or player2.wind == 'E':
if player1.wind == East or player2.wind == East:
efactor = 2
else:
efactor = 1
......
......@@ -61,7 +61,10 @@ class Hand(object):
mjRule is the one out of mjRules with the highest resulting score. Every
hand gets an mjRule even it is not a wining hand, it is the one which
was used for rearranging the hiden tiles to melds."""
was used for rearranging the hiden tiles to melds.
suits include dragons and winds."""
# pylint: disable=too-many-instance-attributes
indent = 0
......@@ -209,7 +212,7 @@ class Hand(object):
@property
def ownWind(self):
"""for easier usage"""
return self.player.wind.lower()
return self.player.wind
@property
def roundWind(self):
......
......@@ -271,6 +271,7 @@ class Job(StrMixin):
def start(self):
"""start this job"""
# pylint: disable=too-many-branches
self.server = Server(self)
# never login to the same server twice at the
# same time with the same player name
......
......@@ -26,6 +26,7 @@ from tile import Tile, TileList
from meld import Meld, MeldList
from common import Internal, Debug, Options, long
from common import unicode, unicodeString
from wind import Wind
from dialogs import Sorry
# pylint: disable=super-init-not-called
......@@ -73,6 +74,8 @@ class Message(object):
cls = value.__class__
if cls in (Tile, TileList, Meld, MeldList):
return str(value).encode()
elif isinstance(value, Wind):
return str(value).encode()
elif isinstance(value, Message):
return value.name
elif isinstance(value, (list, tuple)):
......@@ -433,7 +436,7 @@ class MessageProposeGameId(ServerMessage):
def clientAction(self, client, move):
"""ask the client"""
# move.source are the players in seating order
# move.playerNames are the players in seating order
# we cannot just use table.playerNames - the seating order is now
# different (random)
return client.reserveGameId(move.gameid)
......@@ -470,12 +473,12 @@ class MessageReadyForGameStart(ServerMessage):
client.name)
client.tableList.hide()
return result
# move.source are the players in seating order
# move.playerNames are the players in seating order
# we cannot just use table.playerNames - the seating order is now
# different (random)
return client.readyForGameStart(
move.tableid, move.gameid,
move.wantedGame, move.source, shouldSave=move.shouldSave).addCallback(hideTableList)
move.wantedGame, move.playerNames, shouldSave=move.shouldSave).addCallback(hideTableList)
class MessageNoGameStart(NotifyAtOnceMessage):
......@@ -503,7 +506,7 @@ class MessageReadyForHandStart(ServerMessage):
def clientAction(self, client, move):
"""ask the client"""
return client.readyForHandStart(move.source, move.rotateWinds)
return client.readyForHandStart(move.playerNames, move.rotateWinds)
class MessageInitHand(ServerMessage):
......
......@@ -22,6 +22,7 @@ import weakref
from common import Debug, unicodeString, StrMixin, nativeString
from message import Message
from wind import Wind
from tile import Tile, TileList
from meld import Meld, MeldList
......@@ -57,9 +58,21 @@ class Move(StrMixin):
self.__setattr__(key, MeldList(nativeString(value)))
elif key in ('wantedGame', 'score'):
self.__setattr__(key, nativeString(value))
elif key == 'playerNames':
self.__setattr__(key, self.convertWinds(value))
else:
self.__setattr__(key, value)
@staticmethod
def convertWinds(tuples):
"""convert wind strings to Wind objects"""
if isinstance(tuples[0][0], Wind):
return tuples
result = list()
for wind, name in tuples:
result.append(tuple([Wind(wind), name]))
return result
@property
def player(self):
"""hide weakref"""
......
......@@ -22,8 +22,9 @@ import weakref
from collections import defaultdict
from log import logException, logWarning, m18n, m18nc, m18nE
from common import WINDS, IntDict, Debug, unicode
from common import IntDict, Debug, unicode
from common import StrMixin
from wind import East
from query import Query
from tile import Tile, TileList, elements
from meld import Meld, MeldList
......@@ -133,7 +134,7 @@ class Player(StrMixin):
self.__name = ''
Players.createIfUnknown(name)
self.name = name
self.wind = WINDS[0]
self.wind = East
self.intelligence = AIDefault(self)
self.visibleTiles = IntDict(game.visibleTiles) if game else IntDict()
self.handCache = {}
......@@ -174,7 +175,7 @@ class Player(StrMixin):
"""write once"""
assert self.__name == ''
assert value
assert isinstance(value, unicode)
assert isinstance(value, unicode), 'Player.name must be unicode but not {}'.format(type(value))
self.__name = value
@property
......
......@@ -20,7 +20,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
from tile import Tile, elements
from meld import Meld, MeldList
from common import IntDict, WINDS
from common import IntDict
from wind import East
from message import Message
from query import Query
from permutations import Permutations
......@@ -138,7 +139,7 @@ class ConcealedHonorsKong(RuleCode):
class OwnWindPungKong(RuleCode):
def appliesToMeld(hand, meld):
return meld[0].value == hand.ownWind
return meld[0].value is hand.ownWind
def mayApplyToMeld(meld):
"""for meld rules which depend on context like hand.ownWind, we want
......@@ -152,7 +153,7 @@ class OwnWindPungKong(RuleCode):
class OwnWindPair(RuleCode):
def appliesToMeld(hand, meld):
return meld[0].value == hand.ownWind
return meld[0].value is hand.ownWind
def mayApplyToMeld(meld):
return meld.isPair and meld.isWindMeld
......@@ -161,7 +162,7 @@ class OwnWindPair(RuleCode):
class RoundWindPungKong(RuleCode):
def appliesToMeld(hand, meld):
return meld[0].value == hand.roundWind
return meld[0].value is hand.roundWind
def mayApplyToMeld(meld):
return meld.isPungKong and meld.isWindMeld
......@@ -170,7 +171,7 @@ class RoundWindPungKong(RuleCode):
class RoundWindPair(RuleCode):
def appliesToMeld(hand, meld):
return meld[0].value == hand.roundWind
return meld[0].value is hand.roundWind
def mayApplyToMeld(meld):
return meld.isPair and meld.isWindMeld
......@@ -982,7 +983,7 @@ class ScratchingPole(RuleCode):
class StandardRotation(RuleCode):
def rotate(game):
return game.winner and game.winner.wind != 'E'
return game.winner and game.winner.wind is not East
class EastWonNineTimesInARow(RuleCode):
......@@ -997,12 +998,11 @@ class EastWonNineTimesInARow(RuleCode):
if game.isScoringGame():
# we are only proposing for the last needed Win
needWins -= 1
if game.winner and game.winner.wind == 'E' and game.notRotated >= needWins:
prevailing = WINDS[game.roundsFinished % 4]
if game.winner and game.winner.wind is East and game.notRotated >= needWins:
eastMJCount = int(Query("select count(1) from score "
"where game=%d and won=1 and wind='E' and player=%d "
"and prevailing='%s'" %
(game.gameid, game.players['E'].nameid, prevailing)).records[0][0])
(game.gameid, game.players[East].nameid, game.roundWind.char)).records[0][0])
return eastMJCount == needWins
return False
......@@ -1181,7 +1181,7 @@ class ThirteenOrphans(MJRule):
class OwnFlower(RuleCode):
def appliesToMeld(hand, meld):
return meld[0].value == hand.ownWind
return meld[0].value is hand.ownWind
def mayApplyToMeld(meld):
# pylint: disable=unsubscriptable-object
......@@ -1192,7 +1192,7 @@ class OwnFlower(RuleCode):
class OwnSeason(RuleCode):
def appliesToMeld(hand, meld):
return meld[0].value == hand.ownWind
return meld[0].value is hand.ownWind
def mayApplyToMeld(meld):
# pylint: disable=unsubscriptable-object
......@@ -1203,7 +1203,7 @@ class OwnSeason(RuleCode):
class OwnFlowerOwnSeason(RuleCode):
def appliesToHand(hand):
return sum(x.isBonus and x[0].value == hand.ownWind for x in hand.bonusMelds) == 2
return sum(x.isBonus and x[0].value is hand.ownWind for x in hand.bonusMelds) == 2
class AllFlowers(RuleCode):
......@@ -1265,11 +1265,11 @@ class TwofoldFortune(RuleCode):
class BlessingOfHeaven(RuleCode):
def appliesToHand(hand):
return hand.ownWind == Tile.east and hand.lastSource == '1'
return hand.ownWind is East and hand.lastSource == '1'
def selectable(hand):
"""for scoring game"""
return (hand.ownWind == Tile.east
return (hand.ownWind is East
and hand.lastSource and hand.lastSource in 'wd'
and not hand.announcements - {'a'})
......@@ -1277,11 +1277,11 @@ class BlessingOfHeaven(RuleCode):
class BlessingOfEarth(RuleCode):
def appliesToHand(hand):
return hand.ownWind != Tile.east and hand.lastSource == '1'
return hand.ownWind is not East and hand.lastSource == '1'
def selectable(hand):
"""for scoring game"""
return (hand.ownWind != Tile.east
return (hand.ownWind is not East
and hand.lastSource and hand.lastSource in 'wd'
and not hand.announcements - {'a'})
......
......@@ -21,8 +21,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
from zope.interface import implements # pylint: disable=unused-import
from log import m18n, m18nc, logDebug
from common import LIGHTSOURCES, Internal, isAlive, ZValues, Debug, WINDS
from common import LIGHTSOURCES, Internal, isAlive, ZValues, Debug
from common import nativeString
from wind import Wind
from twisted.internet.defer import succeed
from qt import Qt, QMetaObject, variantValue
......@@ -521,7 +522,7 @@ class ScoringScene(GameScene):
mod = event.modifiers()
key = event.key()
wind = chr(key % 128)
windsX = WINDS + u'X'
windsX = ''.join(x.char for x in Wind.all)
moveCommands = m18nc('kajongg:keyboard commands for moving tiles to the players '
'with wind ESWN or to the central tile selector (X)', windsX)
uiTile = self.focusItem()
......
......@@ -25,7 +25,8 @@ from qt import QGraphicsRectItem, QGraphicsSimpleTextItem
from qt import QPushButton, QMessageBox, QComboBox
from common import Internal, isAlive, WINDS, unicode
from common import Internal, isAlive, unicode
from wind import Wind
from animation import animate
from log import logError, logDebug, logWarning, m18n
from query import Query
......@@ -68,7 +69,7 @@ class SelectPlayers(SelectRuleset):
decorateWindow(self, m18n('Select four players'))
self.names = None
self.nameWidgets = []
for idx, wind in enumerate(WINDS):
for idx, wind in enumerate(Wind.all4):
cbName = QComboBox()
cbName.manualSelect = False
# increase width, we want to see the full window title
......@@ -583,12 +584,11 @@ class ScoringGame(Game):
"VALUES(%d,1,%d,?,?,%d,'%s',%d,'%s','%s',%d,%d,%d,%d,%d)" %
(self.gameid, self.handctr, player.nameid,
scoretime, int(player == self.winner),
WINDS[self.roundsFinished % 4], player.wind, 0,
self.roundWind, player.wind, 0,
amount, player.balance, self.rotated, self.notRotated),
(player.hand.string, offense.name))
Internal.mainWindow.updateGUI()
def scoreGame():
"""show all games, select an existing game or create a new game"""
Players.load()
......@@ -608,4 +608,4 @@ def scoreGame():
selectDialog = SelectPlayers()
if not selectDialog.exec_():
return
return ScoringGame(list(zip(WINDS, selectDialog.names)), selectDialog.cbRuleset.current)
return ScoringGame(list(zip(Wind.all4, selectDialog.names)), selectDialog.cbRuleset.current)
......@@ -44,11 +44,12 @@ from modeltest import ModelTest
from rulesetselector import RuleTreeView
from board import WindLabel, WINDPIXMAPS
from log import m18n, m18nc
from common import WINDS, Internal, Debug, unicode
from common import Internal, Debug, unicode
from statesaver import StateSaver
from query import Query
from guiutil import ListComboBox, Painter, decorateWindow, BlockSignals
from tree import TreeItem, RootItem, TreeModel
from wind import Wind
class ScoreTreeItem(TreeItem):
......@@ -320,8 +321,8 @@ class HandResult(object):
self.notRotated = notRotated
self.penalty = bool(penalty)
self.won = won
self.prevailing = prevailing
self.wind = wind
self.prevailing = Wind(prevailing)
self.wind = Wind(wind)
self.points = points
self.payments = payments
self.balance = balance
......@@ -1052,7 +1053,7 @@ class ScoringDialog(QWidget):
else:
for idx, player in enumerate(self.game.players):
self.windLabels[idx].setPixmap(WINDPIXMAPS[(player.wind,
player.wind == WINDS[self.game.roundsFinished % 4])])
player.wind == self.game.roundWind)])
self.computeScores()
self.spValues[0].setFocus()
self.spValues[0].selectAll()
......
......@@ -23,7 +23,8 @@ from __future__ import print_function
import unittest
from common import Debug, isPython3, WINDS # pylint: disable=unused-import
from common import Debug, isPython3, unicode # pylint: disable=unused-import
from wind import Wind, East, South, West, North
from player import Players
from game import PlayingGame
from hand import Hand, Score
......@@ -45,7 +46,7 @@ for _ in RULESETS[2:]:
Players.createIfUnknown = str
# RULESETS=RULESETS[:1]
GAMES = list([PlayingGame(list(tuple([wind, wind]) for wind in WINDS), x)
GAMES = list([PlayingGame(list(tuple([wind, unicode(wind.char)]) for wind in Wind.all4), x)
for x in RULESETS])
PROGRAM = None
......@@ -84,12 +85,14 @@ class NoWin(Expected):
class Helpers(object):
"""for my test classes"""
# pylint: disable=no-member