I believe the attached patch will solve the problem of tourneys starting with a strange imbalance of players (i.e., starting a tourney of 12 players with 5 per table yields tables of : 5, 5, 2 to start currently).
I solved this by calling balanceGames() at the end of createGames(). I think this is a reasonable solution, although it might be slightly confusing because players might briefly be assigned to the wrong table and switched as the tourney starts. FWIW, I've seen this very behavior on proprietary poker sites. :) I had to rewrite a few tests, which relied on the fact that createGames() did not balanceGames(). There is also a test that exhibits the problem. I should note that I don't think the current table balance routine is as good as it should be. I didn't rewrite it with this patch, though, because it's really a separate problem. The attached patch is also on the branch imbalance-tourney-table-start at r5486. If there are no objections, I'll merge it into trunk sometime next week. Please let me know if you have concerns. I license this patch GPLv3-or-later.
diff --git a/.gitignore b/.gitignore index 07f85e4..67b1297 100644 --- a/.gitignore +++ b/.gitignore @@ -178,6 +178,7 @@ poker-network/tests/test-pokersite.py poker-network/tests/test-pokertable.py poker-network/tests/test-protocol.py poker-network/tests/test-proxyfilter.py +poker-network/tests/test-tourneytablebalance.py poker-network/tests/test-pygame.py poker-network/tests/test-quit.py poker-network/tests/test-servercrash.py diff --git a/poker-engine/ChangeLog b/poker-engine/ChangeLog index ad45764..e6759c5 100644 --- a/poker-engine/ChangeLog +++ b/poker-engine/ChangeLog @@ -1,3 +1,24 @@ +2009-01-31 Bradley M. Kuhn <[email protected]> + + * tests/test-pokertournament.py.in + (PokerTournamentTestCase.testBalanceGames) + (PokerTournamentTestCase.testBreakGames) + (PokerTournamentTestCase.testEqualizeCandidates) + (PokerTournamentTestCase.testEqualizeGames) + (PokerTournamentTestCase.testMovePlayer): Reworked tests based on + fact that balanceGames() is called by default by createGames(). + +2008-12-29 Bradley M. Kuhn <[email protected]> + + * pokerengine/pokertournament.py (PokerTournament.createGames): + Call self.balanceGames() at the end of game creation at start of + the tourney. + + * tests/tournament.py.in + (TestCreate.test4_tourneyTableStartBalanced): Wrote test. + (TestCreate.tourneyTableStartBalancedHelper): Created helper + function. + 2008-12-27 Loic Dachary <[email protected]> * Release 1.3.3 diff --git a/poker-engine/pokerengine/pokertournament.py b/poker-engine/pokerengine/pokertournament.py index 659afb8..5837eaa 100644 --- a/poker-engine/pokerengine/pokertournament.py +++ b/poker-engine/pokerengine/pokertournament.py @@ -448,6 +448,14 @@ class PokerTournament: self.callback_game_filled(self, game) game.close() self.id2game = dict(zip([ game.id for game in self.games ], self.games)) + # Next, need to call balance games, because the table assignment + # algorithm above does not account for scenarios where the last + # few people end up a table too small. This may not be the best + # way to do this, as players may receive information about an + # assignment to a table and quickly moved -- could cause annoying + # client-side. However, this is simpler than a full rewrite of + # the above. + self.balanceGames() def endTurn(self, game_id): game = self.id2game[game_id] diff --git a/poker-engine/tests/test-pokertournament.py.in b/poker-engine/tests/test-pokertournament.py.in index 88d2f84..1e28ef1 100644 --- a/poker-engine/tests/test-pokertournament.py.in +++ b/poker-engine/tests/test-pokertournament.py.in @@ -1,11 +1,8 @@ # -*- mode: python -*- -# Copyright (C) 2006, 2007, 2008 Loic Dachary <[email protected]> -# Copyright (C) 2006 Mekensleep -# -# Mekensleep -# 24 rue vieille du temple -# 75004 Paris -# [email protected] +# Copyright (C) 2006, 2007, 2008 Loic Dachary <[email protected]> +# Copyright (C) 2009 Bradley M. Kuhn <[email protected]> +# Copyright (C) 2006 Mekensleep <[email protected]> +# 24 rue vieille du temple, 75004 Paris # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -471,8 +468,9 @@ class PokerTournamentTestCase(unittest.TestCase): self.failUnless(tournament.register(player)) tournament.createGames() + tournament.removePlayer(tournament.games[0].id, tournament.games[0].serialsAll()[0]) move = pokertournament.equalizeGames(tournament.games) - self.failUnlessEqual(move, [(1, 3, 7), (1, 3, 8), (2, 3, 2)]) + self.failUnlessEqual(move, [(2, 1, 3), (3, 1, 0), (3, 1, 1)]) from_game = tournament.id2game[move[0][0]] to_game = tournament.id2game[move[0][1]] @@ -483,12 +481,12 @@ class PokerTournamentTestCase(unittest.TestCase): player.sit_out = True player.bot = True - self.failUnlessEqual(from_game.allCount(), 5) + self.failUnlessEqual(from_game.allCount(), 4) self.failUnlessEqual(to_game.allCount(), 2) tournament.movePlayer(from_game.id, to_game.id, player.serial) - self.failUnlessEqual(from_game.allCount(), 4) + self.failUnlessEqual(from_game.allCount(), 3) self.failUnlessEqual(to_game.allCount(), 3) player = to_game.getPlayer(move[0][2]) @@ -520,12 +518,21 @@ class PokerTournamentTestCase(unittest.TestCase): want, provide = pokertournament.equalizeCandidates(tournament.games) - self.failUnlessEqual(len(want), 1) - self.failUnlessEqual(len(provide), 2) - - self.failUnlessEqual(want, [[3, 3]]) - self.failUnlessEqual(provide, [(1, [7, 8]), (2, [2, 3])]) - + # Since balanceGames() is called by createGames(), we should find + # these results. + + self.failUnlessEqual(len(want), 0) + self.failUnlessEqual(len(provide), 3) + self.failUnlessEqual(want, []) + self.failUnlessEqual(provide, [(1, []), (2, [3]), (3, [0, 1])]) + + # Remove one from three and he should have one fewer to provide + tournament.removePlayer(tournament.games[2].id, tournament.games[2].serialsAll()[0]) + + want, provide = pokertournament.equalizeCandidates(tournament.games) + self.failUnlessEqual(len(want), 0) + self.failUnlessEqual(len(provide), 3) + self.failUnlessEqual(provide, [(1, []), (2, [3]), (3, [1])]) # ------------------------------------------------------- def testEqualizeGames(self): """Test Poker Tournament : Equalize games""" @@ -547,7 +554,7 @@ class PokerTournamentTestCase(unittest.TestCase): # Create games tournament.createGames() - + # Check games self.failUnlessEqual(len(tournament.games), 2) self.failUnlessEqual(tournament.games[0].allCount(), 5) @@ -556,23 +563,27 @@ class PokerTournamentTestCase(unittest.TestCase): # No need to equalize game self.failUnlessEqual(pokertournament.equalizeGames(tournament.games, 3, None), []) - # Tournament => 3 complete games (5 players), 1 partial (1 players) + # Tournament => 3 , has two tables of 3, then two tables of 5 tournament = pokertournament.PokerTournament(**arguments) for player in range(16): self.failUnless(tournament.register(player)) # Create games tournament.createGames() - + # Check games self.failUnlessEqual(len(tournament.games), 4) - self.failUnlessEqual(tournament.games[0].allCount(), 5) - self.failUnlessEqual(tournament.games[1].allCount(), 5) + self.failUnlessEqual(tournament.games[0].allCount(), 3) + self.failUnlessEqual(tournament.games[1].allCount(), 3) self.failUnlessEqual(tournament.games[2].allCount(), 5) - self.failUnlessEqual(tournament.games[3].allCount(), 1) + self.failUnlessEqual(tournament.games[3].allCount(), 5) - # 2 games broken to complete the last one - self.failUnlessEqual(pokertournament.equalizeGames(tournament.games, int(os.environ.get('VERBOSE_T', 3)), sys.stdout.write), [(1, 4, 11), (1, 4, 12), (2, 4, 6), (2, 4, 7)]) + # Nothing to equalize at first + self.failUnlessEqual(pokertournament.equalizeGames(tournament.games, int(os.environ.get('VERBOSE_T', 3)), sys.stdout.write), []) + + # Remove one player from first table, means players from 3 must be redistributed + tournament.removePlayer(tournament.games[0].id, tournament.games[0].serialsAll()[0]) + self.failUnlessEqual(pokertournament.equalizeGames(tournament.games, int(os.environ.get('VERBOSE_T', 3)), sys.stdout.write), [(3, 1, 1), (3, 1, 2), (4, 1, 0)]) # ------------------------------------------------------- def testBreakGame(self): @@ -646,17 +657,23 @@ class PokerTournamentTestCase(unittest.TestCase): self.failUnlessEqual(len(tournament.games), 3) - self.failUnlessEqual(tournament.games[0].allCount(), 5) - self.failUnlessEqual(tournament.games[1].allCount(), 5) - self.failUnlessEqual(tournament.games[2].allCount(), 2) - + self.failUnlessEqual(tournament.games[0].allCount(), 3) + self.failUnlessEqual(tournament.games[1].allCount(), 4) + self.failUnlessEqual(tournament.games[2].allCount(), 5) + + # This remove leaves us with 2, 4, 5, which means that nothing can be broken. tournament.removePlayer(tournament.games[0].id, tournament.games[0].serialsAll()[0]) - tournament.removePlayer(tournament.games[1].id, tournament.games[1].serialsAll()[0]) - - player1 = tournament.games[2].serialsAll()[0] - player2 = tournament.games[2].serialsAll()[1] + self.failUnlessEqual(pokertournament.breakGames(tournament.games), []) + + # This will leave us with 2, 4, 4, which means table 0 can be broken, and one player + # wil be sent to each table. + tournament.removePlayer(tournament.games[2].id, tournament.games[2].serialsAll()[0]) + + player1 = tournament.games[0].serialsAll()[0] + player2 = tournament.games[0].serialsAll()[1] - self.failUnlessEqual(pokertournament.breakGames(tournament.games), [(3, 2, [player1]), (3, 1, [player2])]) + self.failUnlessEqual(pokertournament.breakGames(tournament.games), + [(1, 3, [player1]), (1, 2, [player2])]) # Impossible to break less than 2 game self.failUnlessEqual(pokertournament.breakGames([tournament.games[0]]), []) @@ -681,32 +698,45 @@ class PokerTournamentTestCase(unittest.TestCase): for player in range(12): self.failUnless(tournament.register(player)) - # Create game, 2 with 5 players, the last one with 2 + # Note: balanceGames() is called by createGames() so initially, + # the tables will be balanced as 3 players at first table, 4 at + # second, and 5 at third. (This doesn't sound all that balanced, + # but it's consistent with the algorithm in + # pokertournament.equalizeGames() + tournament.createGames() - - # Remove one player from the first two games + self.failUnlessEqual(len(tournament.games[0].serialsAll()), 3) + self.failUnlessEqual(len(tournament.games[1].serialsAll()), 4) + self.failUnlessEqual(len(tournament.games[2].serialsAll()), 5) + + # Remove one player from every game. tournament.removePlayer(tournament.games[0].id, tournament.games[0].serialsAll()[0]) tournament.removePlayer(tournament.games[1].id, tournament.games[1].serialsAll()[0]) + tournament.removePlayer(tournament.games[2].id, tournament.games[2].serialsAll()[0]) - # Get the players of the last game - player1 = tournament.games[2].serialsAll()[0] - player2 = tournament.games[2].serialsAll()[1] + # Get the players of the first game + player1 = tournament.games[0].serialsAll()[0] + player2 = tournament.games[0].serialsAll()[1] - # The last game will be break - self.failUnlessEqual(pokertournament.breakGames(tournament.games), [(3, 2, [player1]), (3, 1, [player2])]) + # Ensure that the first game is the one selected for breaking + self.failUnlessEqual(pokertournament.breakGames(tournament.games), + [(1, 3, [player1]), (1, 2, [player2])]) - # The players of the last game are transfered in the other games - self.failUnless(tournament.id2game[3].getPlayer(player1)) + # Test the balancing. First, ensure that the players are still at + # the first game + self.failUnless(tournament.id2game[1].getPlayer(player1)) self.failIf(tournament.id2game[2].getPlayer(player1)) + self.failIf(tournament.id2game[3].getPlayer(player1)) - self.failUnless(tournament.id2game[3].getPlayer(player2)) - self.failIf(tournament.id2game[1].getPlayer(player2)) + self.failUnless(tournament.id2game[1].getPlayer(player2)) + self.failIf(tournament.id2game[2].getPlayer(player2)) + self.failIf(tournament.id2game[3].getPlayer(player2)) self.failUnless(tournament.balanceGames()) self.failUnlessEqual(len(tournament.id2game), 2) - self.failUnless(tournament.id2game[2].getPlayer(player1)) - self.failUnless(tournament.id2game[1].getPlayer(player2)) + self.failUnless(tournament.id2game[3].getPlayer(player1)) + self.failUnless(tournament.id2game[2].getPlayer(player2)) self.failIf(tournament.need_balance) @@ -728,21 +758,26 @@ class PokerTournamentTestCase(unittest.TestCase): for player in range(12): self.failUnless(tournament.register(player)) - # Create game, 2 with 5 players, the last one with 2 + + # As above, note: balanceGames() is called by createGames() so + # initially, the tables will be balanced as 3 players at first + # table, 4 at second, and 5 at third. (This doesn't sound all + # that balanced, but it's consistent with the algorithm in + # pokertournament.equalizeGames() + tournament.createGames() - - # The players are moved from the 2 first games to complete the last one - self.failUnlessEqual(pokertournament.equalizeGames(tournament.games), [(1, 3, 7), (1, 3, 8), (2, 3, 2)]) - - self.failUnless(tournament.balanceGames()) - - # The game 1 has lost 2 players self.failUnlessEqual(len(tournament.games[0].serialsAll()), 3) - # The game 2 has lost 1 players self.failUnlessEqual(len(tournament.games[1].serialsAll()), 4) - # The game 3 has won 3 players self.failUnlessEqual(len(tournament.games[2].serialsAll()), 5) + # ...therefore, no table balancing should be needed ... so + # equalizeGames() should return nothing, and balance games should + # be False. + + self.failUnlessEqual(pokertournament.equalizeGames(tournament.games), []) + self.failIf(tournament.balanceGames()) + + # ------------------------------------------------------- def testEndTurn(self): """Test Poker Tournament : End turn""" @@ -979,7 +1014,7 @@ class Breaks(unittest.TestCase): def GetTestSuite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(PokerTournamentTestCase)) -# suite.addTest(unittest.makeSuite(PokerTournamentTestCase, prefix = "testPrizes")) +# suite.addTest(unittest.makeSuite(PokerTournamentTestCase, prefix = "testBal")) suite.addTest(unittest.makeSuite(Breaks)) # Comment out above and use line below this when you wish to run just # one test by itself (changing prefix as needed). diff --git a/poker-engine/tests/tournament.py.in b/poker-engine/tests/tournament.py.in index 7889feb..9a8be6f 100644 --- a/poker-engine/tests/tournament.py.in +++ b/poker-engine/tests/tournament.py.in @@ -436,7 +436,36 @@ class TestCreate(unittest.TestCase): if self.verbose > 0: for serial in tourney.winners: print "%d\thas rank %d" % ( serial, tourney.getRank(serial) ) + # ---------------------------------------------------------------- + def tourneyTableStartBalancedHelper(self, num_players, seats, num_tables, min_per_table): + """tourneyTableStartBalancedHelper + Helper function to test various scenarios of initial seating""" + tourney = PokerTournament(name = 'Only%d' % num_players, + verbose = int(os.environ.get('VERBOSE_T', 3)), + players_quota = num_players, + players_min = num_players, + dirs = [ '../conf', '@top_srcdir@/conf' ], + seats_per_game = seats, + betting_structure = "level-10-20-no-limit") + + for serial in xrange(1,num_players+1): + self.failUnless(tourney.register(serial)) + + self.assertEquals(len(tourney.games), num_tables) + for game in tourney.games: + self.failUnless(len(game.serial2player.values()) >= min_per_table) + # ---------------------------------------------------------------- + def test4_tourneyTableStartBalanced(self): + """test4_tourneyTableStartBalanced + + Start with a table max of 5 players per table, and add 6 players + to it. This should create two tables of 3. Tests other scenarios + like this as well.""" + + self.tourneyTableStartBalancedHelper(6, 5, 2, 2) + self.tourneyTableStartBalancedHelper(13, 6, 3, 4) +# ---------------------------------------------------------------- class TestPrizes(unittest.TestCase): def test1(self): @@ -519,7 +548,7 @@ def run(): suite.addTest(unittest.makeSuite(TestPrizes)) # Comment out above and use line below this when you wish to run just # one test by itself (changing prefix as needed). -# suite.addTest(unittest.makeSuite(TestPrizes, prefix = "test2")) +# suite.addTest(unittest.makeSuite(TestCreate, prefix = "test4")) verbosity = int(os.environ.get('VERBOSE_T', 2)) return unittest.TextTestRunner(verbosity=verbosity).run(suite) diff --git a/poker-network/ChangeLog b/poker-network/ChangeLog index 5ad3aa9..3d692eb 100644 --- a/poker-network/ChangeLog +++ b/poker-network/ChangeLog @@ -1,3 +1,9 @@ +2008-12-28 Bradley M. Kuhn <[email protected]> + + * tests/test-tourneytablebalance.py.in + (TourneyTableBalanceTestCase.test01_sixPlayersTourney5PerTable): + Wrote test. + 2008-12-27 Loic Dachary <[email protected]> * Release 1.7.4 diff --git a/poker-network/configure.ac b/poker-network/configure.ac index beee56b..10fe2cb 100644 --- a/poker-network/configure.ac +++ b/poker-network/configure.ac @@ -335,6 +335,7 @@ AC_CONFIG_FILES([ tests/test-pokerpackets.py tests/test-pokerclientpackets.py tests/test-pokerservice.py + tests/test-tourneytablebalance.py tests/test-pokersite.py tests/test-pokermemcache.py tests/test-pokerauth.py diff --git a/poker-network/tests/test-tourneytablebalance.py.in b/poker-network/tests/test-tourneytablebalance.py.in new file mode 100644 index 0000000..a049730 --- /dev/null +++ b/poker-network/tests/test-tourneytablebalance.py.in @@ -0,0 +1,297 @@ +...@python@ +# -*- mode: python; coding: iso-8859-1 -*- +# more information about the above line at http://www.python.org/dev/peps/pep-0263/ +# +# Copyright (C) 2008 Bradley M. Kuhn <[email protected]> +# Copyright (C) 2007, 2008 Loic Dachary <[email protected]> +# Copyright (C) 2006 Mekensleep <[email protected]> +# 24 rue vieille du temple 75004 Paris +# +# This software's license gives you freedom; you can copy, convey, +# propagate, redistribute and/or modify this program under the terms of +# the GNU Affero General Public License (AGPL) as published by the Free +# Software Foundation (FSF), either version 3 of the License, or (at your +# option) any later version of the AGPL published by the FSF. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +# General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program in a file in the toplevel directory called +# "AGPLv3". If not, see <http://www.gnu.org/licenses/>. +# +# Authors: +# Bradley M. Kuhn <[email protected]> +# +import sys, os +sys.path.insert(0, "@srcdir@/..") +sys.path.insert(0, "..") + +import time + +from string import split +import libxml2 +import random +import locale +import sets +from _mysql_exceptions import IntegrityError +from pprint import pprint +from datetime import date + +from tests import testclock + +from twisted.trial import unittest, runner, reporter +import twisted.internet.base +from twisted.internet import reactor, defer + +twisted.internet.base.DelayedCall.debug = True + +from tests.testmessages import silence_all_messages, search_output, clear_all_messages, get_messages +verbose = int(os.environ.get('VERBOSE_T', '-1')) +silence_all_messages() + +from pokerengine import pokertournament, pokergame +from pokernetwork import pokerservice, pokernetworkconfig, user +from pokernetwork import currencyclient +from pokernetwork import pokerdatabase +currencyclient.CurrencyClient = currencyclient.FakeCurrencyClient +from pokernetwork.pokerpackets import * +from pokernetwork.packets import PacketError +from pokernetwork.userstats import UserStatsRankPercentileLookup +from pokernetwork.tourneyattrs import TourneyAttrsSponsoredPrizesLookup +from pokernetwork.attrpack import AttrsLookup +from MySQLdb.cursors import DictCursor + +class ConstantDeckShuffler: + def shuffle(self, what): + what[:] = [40, 13, 32, 9, 19, 31, 15, 14, 50, 34, 20, 6, 43, 44, 28, 29, 48, 3, 21, 45, 23, 37, 35, 11, 5, 22, 24, 30, 27, 39, 46, 33, 0, 8, 1, 42, 36, 16, 49, 2, 10, 26, 4, 18, 7, 41, 47, 17] + +from pokerengine import pokergame +pokergame.shuffler = ConstantDeckShuffler() + +class ConstantPlayerShuffler: + def shuffle(self, what): + what.sort() + +from pokerengine import pokertournament +pokertournament.shuffler = ConstantPlayerShuffler() + +settings_xml = """<?xml version="1.0" encoding="ISO-8859-1"?> +<server verbose="6" ping="300000" autodeal="yes" max_joined="1000" simultaneous="4" chat="yes" remove_completed="1" > + <delays autodeal="18" round="12" position="60" showdown="30" finish="18" /> + + <table name="Table1" variant="holdem" betting_structure="100-200-no-limit" seats="10" player_timeout="60" currency_serial="1" /> + <table name="Table2" variant="holdem" betting_structure="100-200-no-limit" seats="10" player_timeout="60" currency_serial="1" /> + + <listen tcp="19480" /> + + <language value="en_US.ISO-8859-1"/> + + <stats type="RankPercentile"/> + + <tourney_attrs type="SponsoredPrizes"/> + + <refill serial="1" amount="10000000" /> + + <cashier acquire_timeout="5" pokerlock_queue_timeout="30" user_create="yes" /> + <database name="pokernetworktest" host="@MYSQL_TEST_DBHOST@" user="pokernetworktest" password="pokernetwork" + root_user="@MYSQL_TEST_DBROOT@" root_password="@MYSQL_TEST_DBROOT_PASSWORD@" schema="@srcdir@/../../database/schema.sql" command="@MYSQL@" /> + <path>@POKER_ENGINE_PKGSYSCONFDIR@ @POKER_NETWORK_PKGSYSCONFDIR@</path> + <users temporary="BOT"/> +</server> +""" +class UserMockup: + def isLogged(self): return True + +class ClientMockup: + def __init__(self, serial): + self.user = UserMockup() + self.serial = serial + self.packet_end_tournament = None + self.packets = [] + self.tables = {} + self.joinedTables = [] + self.deferred = None + + def getPlayerInfo(self): + class MockInfo: + def __init__(miSelf): + miSelf.name = "PLAYER INFO: %d" % self.serial + miSelf.url = "http://example.org" + miSelf.outfit = "naked" + return MockInfo() + + def sendPacket(self, packet): + self.packets.append(packet) + if self.deferred and self.type == packet.type: + reactor.callLater(0, lambda: self.deferred.callback(packet)) + + def join(self, table): + self.joinedTables.append(table) + self.tables[table.game.id] = table + + def getSerial(self): + return self.serial + + def sendPacketVerbose(self, packet): + self.sendPacket(packet) + + def waitFor(self, type): + self.deferred = defer.Deferred() + self.type = type + return self.deferred + +class TourneyTableBalanceTestCase(unittest.TestCase): + + def destroyDb(self): + if len("@MYSQL_TEST_DBROOT_PASSWORD@") > 0: + os.system("@MYSQL@ -u @MYSQL_TEST_DBROOT@ --password='@MYSQL_TEST_DBROOT_PASSWORD@' -h '@MYSQL_TEST_DBHOST@' -e 'DROP DATABASE IF EXISTS pokernetworktest'") + else: + os.system("@MYSQL@ -u @MYSQL_TEST_DBROOT@ -h '@MYSQL_TEST_DBHOST@' -e 'DROP DATABASE IF EXISTS pokernetworktest'") + + # ---------------------------------------------------------------- + def setUp(self): + testclock._seconds_reset() + self.destroyDb() + self.settings = settings = pokernetworkconfig.Config([]) + settings.doc = libxml2.parseMemory(settings_xml, len(settings_xml)) + settings.header = settings.doc.xpathNewContext() + self.db = pokerdatabase.PokerDatabase(settings) + self.service = pokerservice.PokerService(settings) + self.default_money = 10000000 +# self.service.verbose = 0 +# self.service.verbose = 4 + # ---------------------------------------------------------------- + def tearDown(self): + self.db.close() + d = self.service.stopService() + d.addCallback(lambda x: self.destroyDb()) + return d + # ---------------------------------------------------------------- + def createUsers(self): + cursor = self.db.cursor() + for user_number in (1, 2, 3, 4, 5, 6): + cursor.execute("INSERT INTO users (name, password, created) VALUES ('user%d', 'password%d', 0)" % ( user_number, user_number )) + self.assertEqual(1, cursor.rowcount) + + self.user_serials = [] + for ii in range(0,6): self.user_serials.append(None) + ( (self.user_serials[0], name, privilege), message ) = self.service.auth("user1", "password1", "role1") + ( (self.user_serials[1], name, privilege), message ) = self.service.auth("user2", "password2", "role1") + ( (self.user_serials[2], name, privilege), message ) = self.service.auth("user3", "password3", "role1") + ( (self.user_serials[3], name, privilege), message ) = self.service.auth("user4", "password4", "role1") + ( (self.user_serials[4], name, privilege), message ) = self.service.auth("user5", "password5", "role1") + ( (self.user_serials[5], name, privilege), message ) = self.service.auth("user6", "password6", "role1") + +# for user_number in self.user_serials: +# if self.default_money > 0 and user_number == self.user3_serial: +# cursor.execute("INSERT INTO user2money (user_serial, currency_serial, amount) VALUES (%d, 2, %d)" % ( user_number, self.default_money ) ) +# self.assertEqual(1, cursor.rowcount) + + cursor.close() + # ---------------------------------------------------------------- + def test01_sixPlayersTourney5PerTable(self): + """test01_sixPlayersTourney5PerTable + + Test the condition where six players sign up for a tourney that + has five people per table. It has been reported that this causes + 5 people at one table and 1 player at the other""" + + pokerservice.UPDATE_TOURNEYS_SCHEDULE_DELAY = 1 + pokerservice.CHECK_TOURNEYS_SCHEDULE_DELAY = 0.1 + + cursor = self.db.cursor() + cursor.execute(""" +INSERT INTO `tourneys_schedule` ( `name`, `description_short` , `description_long` , `players_quota` , `variant` , `betting_structure` , `seats_per_game` , `currency_serial` , `buy_in` , `rake` , `sit_n_go` , `start_time` , `register_time` , `respawn` , `respawn_interval`, `players_min`, `breaks_first` ) +VALUES ( 'Only6', 'Sit and Go 6 players and only 6 , Holdem', 'Sit and Go 6 players only', '6', 'holdem', 'level-15-30-no-limit', '5', 1, '0', '0', 'y', '0', '0', 'y', '0', '6', 1 ); +""") + cursor.close() +#INSERT INTO `tourneys_schedule` (`name`, `description_short`, `description_long`, `players_quota`, `variant`, `betting_structure`, `seats_per_game`, `currency_serial`, `buy_in`, `rake`, `sit_n_go`, `breaks_interval`, `rebuy_delay`, `add_on`, `add_on_delay`, `start_time`, `register_time`, `respawn`, `respawn_interval`, `players_min`) VALUES ('Only6', 'Only 6 Freeroll', 'Only 6 No Limit Freeroll', '6', 'holdem', 'level-001', '5', 1, '0', '0', 'y', '60', '30', '1', '60', unix_timestamp(now() + INTERVAL 2 MINUTE), unix_timestamp(now() - INTERVAL 1 HOUR), 'n', '0', '6'); + + self.service.startService() + self.createUsers() + tourneys = self.service.tourneySelect('Only6') + self.assertEquals(len(tourneys), 1) + t = tourneys[0] + self.assertEquals(t['name'], 'Only6') + self.assertEquals(t['betting_structure'], 'level-15-30-no-limit') + self.assertEquals(t['players_quota'], 6L) + self.assertEquals(t['players_min'], 6L) + self.assertEquals(t['seats_per_game'], 5L) + tourneySerial = t['serial'] + + clients = {} + for userSerial in self.user_serials: + clients[userSerial] = ClientMockup(userSerial) + self.service.serial2client[userSerial] = clients[userSerial] + self.service.tourneyRegister(PacketPokerTourneyRegister(serial = userSerial, + game_id = tourneySerial)) + + tourneys = self.service.tourneys.values() + + (sixTourney,) = filter(lambda tourney: tourney.name == 'Only6', self.service.tourneys.values()) + self.assertEquals(sixTourney.serial, tourneySerial) + + + d = defer.Deferred() + def checkTourney(status): + self.assertEquals(pokertournament.TOURNAMENT_STATE_RUNNING, sixTourney.state) + + self.assertEquals(self.service.joined_count, 6) + + for game in sixTourney.games: + print game.__dict__ + # Here is what exhibits the problem. Some of the tables + # don't have more than 1 person at them! + self.failUnless(len(game.serial2player.keys()) >= 2) + + for game in sixTourney.games: + # This code is needed to make sure the test doesn't + # timeout with a reactor error bedcause the players + # timeout. There may be an easier way to do that.... + if len(game.serial2player.keys()) > 1: + table = self.service.getTable(game.id) + in_position = game.getSerialInPosition() + game.callNraise(in_position, game.maxBuyIn()) + in_position = game.getSerialInPosition() + game.call(in_position) + in_position = game.getSerialInPosition() + game.call(in_position) + in_position = game.getSerialInPosition() + game.call(in_position) + in_position = game.getSerialInPosition() + game.call(in_position) + table.update() + + d.addCallback(checkTourney) + + reactor.callLater(3, lambda: d.callback(True)) + + return d +# ---------------------------------------------------------------- +def Run(): + loader = runner.TestLoader() + loader.methodPrefix = "test01" + suite = loader.suiteFactory() + suite.addTest(loader.loadClass(TourneyTableBalanceTestCase)) + return runner.TrialRunner( + reporter.VerboseTextReporter, +# reporter.TextReporter, +# tracebackFormat='verbose', + tracebackFormat='default', + ).run(suite) + +# ---------------------------------------------------------------- +if __name__ == '__main__': + if Run().wasSuccessful(): + sys.exit(0) + else: + sys.exit(1) + +# Interpreted by emacs +# Local Variables: +# compile-command: "( cd .. ; ./config.status tests/test-tourneytablebalance.py ) ; ( cd ../tests ; make COVERAGE_FILES='' TESTS='test-tourneytablebalance.py' check )" +# End: +
-- -- bkuhn
_______________________________________________ Pokersource-users mailing list [email protected] https://mail.gna.org/listinfo/pokersource-users
