[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [kajongg/sid] src: move intelligence from Client to Player
From:       Wolfgang Rohdewald <wolfgang () rohdewald ! de>
Date:       2013-10-31 21:21:04
Message-ID: E1Vbzfs-0002lj-F2 () scm ! kde ! org
[Download RAW message or body]

Git commit 286e57a5c0b4e4ae6a38f2438fd544a71e2f5fd9 by Wolfgang Rohdewald.
Committed on 29/10/2013 at 10:14.
Pushed by wrohdewald into branch 'sid'.

move intelligence from Client to Player

M  +24   -7    src/client.py
M  +1    -15   src/game.py
M  +6    -18   src/humanclient.py
M  +27   -32   src/intelligence.py
M  +2    -0    src/player.py
M  +1    -2    src/server.py

http://commits.kde.org/kajongg/286e57a5c0b4e4ae6a38f2438fd544a71e2f5fd9

diff --git a/src/client.py b/src/client.py
index 9215547..b7dd18c 100644
--- a/src/client.py
+++ b/src/client.py
@@ -29,17 +29,18 @@ from twisted.internet.error import ReactorNotRunning
 from twisted.python.failure import Failure
 from util import logDebug, logException, logWarning, Duration, m18nc, checkMemory
 from message import Message
-from common import Internal, Debug
+from common import Internal, Debug, Options
 from rule import Ruleset
 from meld import meldsContent
 from game import PlayingGame
 from query import Transaction, Query
 from move import Move
 from animation import animate
-from intelligence import AIDefault
 from statesaver import StateSaver
 from player import PlayingPlayer
 
+import intelligence, altint
+
 class Table(object):
     """defines things common to both ClientTable and ServerTable"""
     def __init__(self, tableid, ruleset, suspendedAt, running, playOpen, autoPlay, \
wantedGame): @@ -119,11 +120,10 @@ class Client(object, pb.Referenceable):
     def __del__(self):
         self.game = None
 
-    def __init__(self, name=None, intelligence=AIDefault):
+    def __init__(self, name=None):
         """name is something like Robot 1 or None for the game server"""
         self.name = name
         self.game = None
-        self.intelligence = intelligence(self)
         self.__connection = None
         self.tables = []
         self.table = None
@@ -132,7 +132,6 @@ class Client(object, pb.Referenceable):
     def delete(self):
         """for better garbage collection"""
         self.table = None
-        self.intelligence = None
 
     @property
     def connection(self):
@@ -252,6 +251,22 @@ class Client(object, pb.Referenceable):
                 return Message.NO
         return Message.OK
 
+    @staticmethod
+    def __findAI(modules, aiName):
+        """list of all alternative AIs defined in altint.py"""
+        for modul in modules:
+            for key, value in modul.__dict__.items():
+                if key == 'AI' + aiName:
+                    return value
+
+    def __assignIntelligence(self):
+        """assign intelligence to myself. All players already have default \
intelligence.""" +        if self.isHumanClient():
+            aiClass = self.__findAI([intelligence, altint], Options.AI)
+            if not aiClass:
+                raise Exception('intelligence %s is undefined' % Options.AI)
+            self.game.myself.intelligence = aiClass(self.game.myself)
+
     def readyForGameStart(self, tableid, gameid, wantedGame, playerNames, \
                shouldSave=True, gameClass=None):
         """the game server asks us if we are ready. A robot is always ready."""
         def disagree(about):
@@ -283,6 +298,7 @@ class Client(object, pb.Referenceable):
             self.game = gameClass(playerNames, self.table.ruleset,
                 shouldSave=shouldSave, gameid=gameid, wantedGame=wantedGame, \
                client=self,
                 playOpen=self.table.playOpen, autoPlay=self.table.autoPlay)
+        self.__assignIntelligence()  # intelligence variant is not saved for \
suspended games  self.game.prepareHand()
         return succeed(Message.OK)
 
@@ -321,8 +337,9 @@ class Client(object, pb.Referenceable):
         sends answer and one parameter to server"""
         delay = 0.0
         delayStep = 0.1
-        self.game.myself.computeSayable(move, answers)
-        result = self.intelligence.selectAnswer(answers)
+        myself = self.game.myself
+        myself.computeSayable(move, answers)
+        result = myself.intelligence.selectAnswer(answers)
         if result[0] == Message.Chow:
             # self.game.debug('%s waits to see if somebody says Pung or Kong before \
saying chow' %  #     self.game.myself.name)
diff --git a/src/game.py b/src/game.py
index 9ab98c6..f800cde 100644
--- a/src/game.py
+++ b/src/game.py
@@ -116,7 +116,6 @@ class Game(object):
         # pylint: disable=too-many-statements
         assert self.__class__ != Game, 'Do not directly instantiate Game'
         self.players = Players() # if we fail later on in init, at least we can \
                still close the program
-        self._client = None
         self.client = client
         self.rotated = 0
         self.notRotated = 0 # counts hands since last rotation
@@ -204,19 +203,6 @@ class Game(object):
                 raise Exception('lastDiscard is lower:%s' % value)
 
     @property
-    def client(self):
-        """hide weakref"""
-        if self._client is not None:
-            return self._client()
-    @client.setter
-    def client(self, value):
-        """hide weakref"""
-        if value is None:
-            self._client = None
-        else:
-            self._client = weakref.ref(value)
-
-    @property
     def winner(self):
         """the name of the game server this game is attached to"""
         return self.__winner
@@ -244,7 +230,7 @@ class Game(object):
         """identifies the hand for window title and scoring table"""
         aiVariant = ''
         if withAI and self.belongsToHumanPlayer():
-            aiName = self.client.intelligence.name()
+            aiName = self.myself.intelligence.name()
             if aiName != 'Default':
                 aiVariant = aiName + '/'
         num = self.notRotated
diff --git a/src/humanclient.py b/src/humanclient.py
index 81a48ec..8843cf2 100644
--- a/src/humanclient.py
+++ b/src/humanclient.py
@@ -41,8 +41,6 @@ from board import Board
 from client import Client, ClientTable
 from tables import TableList, SelectRuleset
 from sound import Voice
-import intelligence
-import altint
 from login import Connection
 from rule import Ruleset
 from game import PlayingGame
@@ -225,7 +223,7 @@ class ClientDialog(QDialog):
         result = self.buttons[0]
         game = self.client.game
         if game.autoPlay or Preferences.propose:
-            answer, parameter = self.client.intelligence.selectAnswer(
+            answer, parameter = game.myself.intelligence.selectAnswer(
                 self.messages())
             result = [x for x in self.buttons if x.message == answer][0]
             result.setFocus()
@@ -340,10 +338,7 @@ class HumanClient(Client):
     # pylint: disable=too-many-public-methods
     humanClients = []
     def __init__(self):
-        aiClass = self.__findAI([intelligence, altint], Options.AI)
-        if not aiClass:
-            raise Exception('intelligence %s is undefined' % Options.AI)
-        Client.__init__(self, intelligence=aiClass)
+        Client.__init__(self)
         HumanClient.humanClients.append(self)
         self.table = None
         self.ruleset = None
@@ -422,14 +417,6 @@ class HumanClient(Client):
         """as the name says"""
         Internal.field.startingGame = False
 
-    @staticmethod
-    def __findAI(modules, aiName):
-        """list of all alternative AIs defined in altint.py"""
-        for modul in modules:
-            for key, value in modul.__dict__.items():
-                if key == 'AI' + aiName:
-                    return value
-
     def isRobotClient(self):
         """avoid using isinstance, it would import too much for kajonggserver"""
         return False
@@ -634,12 +621,13 @@ class HumanClient(Client):
         Since we might return a Deferred to be sent to the server,
         which contains Message.Chow plus selected Chow, we should
         return the same tuple here"""
+        intelligence = self.game.myself.intelligence
         if self.game.autoPlay:
-            return Message.Chow, self.intelligence.selectChow(chows)
+            return Message.Chow, intelligence.selectChow(chows)
         if len(chows) == 1:
             return Message.Chow, chows[0]
         if Preferences.propose:
-            propose = self.intelligence.selectChow(chows)
+            propose = intelligence.selectChow(chows)
         else:
             propose = None
         deferred = Deferred()
@@ -650,7 +638,7 @@ class HumanClient(Client):
     def __selectKong(self, kongs):
         """which possible kong do we want to declare?"""
         if self.game.autoPlay:
-            return Message.Kong, self.intelligence.selectKong(kongs)
+            return Message.Kong, self.game.myself.intelligence.selectKong(kongs)
         if len(kongs) == 1:
             return Message.Kong, kongs[0]
         deferred = Deferred()
diff --git a/src/intelligence.py b/src/intelligence.py
index a59d2aa..8634a20 100644
--- a/src/intelligence.py
+++ b/src/intelligence.py
@@ -32,8 +32,8 @@ class AIDefault(object):
     # we could solve this by moving those filters into DiscardCandidates
     # but that would make it more complicated to define alternative AIs
 
-    def __init__(self, client):
-        self.client = client
+    def __init__(self, player):
+        self.player = player
 
     def name(self):
         """return our name"""
@@ -65,15 +65,15 @@ class AIDefault(object):
         Much of this is just trial and success - trying to get as much AI
         as possible with limited computing resources, it stands on
         no theoretical basis"""
-        candidates = DiscardCandidates(self.client.game, hand)
+        candidates = DiscardCandidates(self.player, hand)
         result = self.weighDiscardCandidates(candidates).best()
         candidates.unlink()
         return result
 
     def weighDiscardCandidates(self, candidates):
         """the standard"""
-        game = self.client.game
-        weighFunctions = self.client.game.ruleset.filterFunctions('weigh')
+        game = self.player.game
+        weighFunctions = game.ruleset.filterFunctions('weigh')
         for aiFilter in [self.weighBasics, self.weighSameColors,
                 self.weighSpecialGames, self.weighCallingHand,
                 self.weighOriginalCall,
@@ -173,20 +173,18 @@ class AIDefault(object):
 
     def respectOriginalCall(self):
         """True if we said CaO and can still win without violating it"""
-        game = self.client.game
-        myself = game.myself
-        if not myself.originalCall or not myself.mayWin:
+        if not self.player.originalCall or not self.player.mayWin:
             return False
-        result = self.chancesToWin(myself.originalCallingHand)
+        result = self.chancesToWin(self.player.originalCallingHand)
         if not result:
-            myself.mayWin = False # bad luck
+            self.player.mayWin = False # bad luck
         return result
 
     @staticmethod
     def weighOriginalCall(aiInstance, candidates):
         """if we declared Original Call, respect it"""
-        game = aiInstance.client.game
-        myself = game.myself
+        myself = aiInstance.player
+        game = myself.game
         if myself.originalCall and myself.mayWin:
             if Debug.originalCall:
                 game.debug('weighOriginalCall: lastTile=%s, candidates=%s' %
@@ -223,14 +221,14 @@ class AIDefault(object):
         answer = parameter = None
         tryAnswers = (x for x in [Message.MahJongg, Message.OriginalCall, \
Message.Kong,  Message.Pung, Message.Chow, Message.Discard] if x in answers)
-        hand = self.client.game.myself.hand
+        hand = self.player.hand
         claimness = IntDict()
-        discard = self.client.game.lastDiscard
+        discard = self.player.game.lastDiscard
         if discard:
-            for func in self.client.game.ruleset.filterFunctions('claimness'):
+            for func in self.player.game.ruleset.filterFunctions('claimness'):
                 claimness += func.claimness(hand, discard)
         for tryAnswer in tryAnswers:
-            parameter = self.client.game.myself.sayable[tryAnswer]
+            parameter = self.player.sayable[tryAnswer]
             if not parameter:
                 continue
             if claimness[tryAnswer] < 0:
@@ -241,7 +239,7 @@ class AIDefault(object):
                 parameter = self.selectDiscard(hand)
             elif tryAnswer in [Message.Pung, Message.Chow, Message.Kong] and \
self.respectOriginalCall():  continue
-            elif tryAnswer == Message.Pung and \
self.client.game.myself.maybeDangerous(tryAnswer): +            elif tryAnswer == \
Message.Pung and self.player.maybeDangerous(tryAnswer):  continue
             elif tryAnswer == Message.Chow:
                 parameter = self.selectChow(parameter)
@@ -256,16 +254,14 @@ class AIDefault(object):
 
     def selectChow(self, chows):
         """selects a chow to be completed. Add more AI here."""
-        game = self.client.game
-        myself = game.myself
         for chow in chows:
             # a robot should never play dangerous
-            if not myself.mustPlayDangerous(chow):
-                if not myself.hasConcealedTiles(chow):
+            if not self.player.mustPlayDangerous(chow):
+                if not self.player.hasConcealedTiles(chow):
                     # do not dissolve an existing chow
                     belongsToPair = False
                     for tile in chow:
-                        if myself.concealedTileNames.count(tile) == 2:
+                        if self.player.concealedTileNames.count(tile) == 2:
                             belongsToPair = True
                             break
                     if not belongsToPair:
@@ -274,7 +270,7 @@ class AIDefault(object):
     def selectKong(self, kongs):
         """selects a kong to be declared. Having more than one undeclared kong is \
quite improbable"""  for kong in kongs:
-            if not self.client.game.myself.mustPlayDangerous(kong):
+            if not self.player.mustPlayDangerous(kong):
                 return kong
 
     def chancesToWin(self, hand):
@@ -282,7 +278,7 @@ class AIDefault(object):
         result = []
         for completedHand in hand.callingHands(99):
             result.extend([completedHand.lastTile] * (
-                    self.client.game.myself.tileAvailable(completedHand.lastTile, \
hand))) +                    self.player.tileAvailable(completedHand.lastTile, \
hand)))  return result
 
     def xxxxhandValue(self):
@@ -294,8 +290,7 @@ class AIDefault(object):
         visible, chances for MJ are 0.
         This will become the central part of AI -
         moves will be done which optimize the hand value"""
-        game = self.client.game
-        hand = game.myself.hand
+        hand = self.player.hand
         assert not hand.handlenOffset(), hand
         result = 0
         if hand.won:
@@ -330,9 +325,9 @@ class TileAI(object):
         self.group, self.value = name[:2]
         if self.value in '123456789bgreswn' and len(name) == 2:
             self.occurrence = candidates.hiddenTiles.count(name)
-            self.available = candidates.game.myself.tileAvailable(name, \
candidates.hand) +            self.available = candidates.player.tileAvailable(name, \
candidates.hand)  self.maxPossible = self.available + self.occurrence
-            self.dangerous = \
bool(candidates.game.dangerousFor(candidates.game.myself, name)) +            \
self.dangerous = bool(candidates.player.game.dangerousFor(candidates.player, name))  \
else:  # value might be -1, 0, 10, 11 for suits
             self.occurrence = 0
@@ -355,9 +350,9 @@ class TileAI(object):
 class DiscardCandidates(list):
     """a list of TileAI objects. This class should only hold
     AI neutral methods"""
-    def __init__(self, game, hand):
+    def __init__(self, player, hand):
         list.__init__(self)
-        self.game = game
+        self.player = player
         self.hand = hand
         self.hiddenTiles = list(x.lower() for x in hand.tilesInHand)
         self.groupCounts = IntDict() # counts for tile groups (sbcdw), exposed and \
concealed @@ -414,7 +409,7 @@ class DiscardCandidates(list):
         """returns the candidate with the lowest value"""
         lowest = min(x.keep for x in self)
         candidates = sorted(list(x for x in self if x.keep == lowest), key=lambda x: \
                x.name)
-        result = self.game.randomGenerator.choice(candidates).name.capitalize()
+        result = self.player.game.randomGenerator.choice(candidates).name.capitalize()
  if Debug.robotAI:
-            self.game.debug('%s: discards %s out of %s' % (self.game.myself, result, \
' '.join(str(x) for x in self))) +            self.player.game.debug('%s: discards %s \
out of %s' % (self.player, result, ' '.join(str(x) for x in self)))  return result
diff --git a/src/player.py b/src/player.py
index 496c380..6a0ca29 100644
--- a/src/player.py
+++ b/src/player.py
@@ -28,6 +28,7 @@ from tile import Tile, elements
 from meld import Meld, CONCEALED, PUNG, hasChows, meldsContent
 from message import Message
 from hand import Hand
+from intelligence import AIDefault
 
 class Players(list):
     """a list of players where the player can also be indexed by wind.
@@ -118,6 +119,7 @@ class Player(object):
         self.wonCount = 0
         self.__name = ''
         self.wind = WINDS[0]
+        self.intelligence = AIDefault(self)
         self.visibleTiles = IntDict(game.visibleTiles) if game else IntDict()
         self.clearHand()
         self.__lastSource = '1' # no source: blessing from heaven or earth
diff --git a/src/server.py b/src/server.py
index 51c22d7..446597b 100644
--- a/src/server.py
+++ b/src/server.py
@@ -256,9 +256,8 @@ class ServerTable(Table):
             m18ncE('kajongg, name of robot player, to be translated', 'Robot 3')]
         while len(names) < 4:
             names.append(robotNames[3 - len(names)])
-        result = PlayingGame(names, self.ruleset, client=Client(),
+        return PlayingGame(names, self.ruleset, client=Client(),
             playOpen=self.playOpen, autoPlay=self.autoPlay, \
                wantedGame=self.wantedGame, shouldSave=True)
-        return result
 
     def userForPlayer(self, player):
         """finds the table user corresponding to player"""


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic