[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