Exemple #1
0
	def prepareGame(self):
		if self.camera:
			# Disable Mouse Control for camera
			self.disableMouse()
			
			self.camera.setPos(0, 0, 500)
			self.camera.lookAt(0, 0, 0)

		self.gameData = GameData(True)
		
		# game data
		self.broadcastData(('gamedata', self.gameData.packageData()))
		self.broadcastData(('state', 'preround'))
		if self.online:
			self.client.sendData(('state', 'preround'))
		print "Preparing Game"
		self.gameTime = 0
		self.tick = 0

		usersData = []
		for user in self.users:
			usersData.append(user.gameData)
		self.game = Game(self, usersData, self.gameData)
		self.taskMgr.doMethodLater(0.5, self.roundReadyLoop, 'Game Loop')
		print "Round ready State"
Exemple #2
0
def main(*flags):
    """Main game loop and event handling."""
    
    # Initialization
    pygame.init()
    #Put window in center of screen
    os.environ ['SDL_VIDEO_WINDOW_POS'] = 'center'
    
    screen = pygame.display.set_mode(SCREEN_SIZE,
                                     pygame.FULLSCREEN if FULLSCREEN else 0)
    pygame.display.set_caption('Jeopardy!')

    # Declarations
    gameData = GameData()
    gs = JeopGameState()
    uicontroller = Controller(screen, gameData, FPS_LIMIT)
    clock = pygame.time.Clock()
    
    # Intro sequence (control passed completely to functions)
    if SKIP_INTRO_FLAG not in flags:
        pygame.mouse.set_visible(0)
        do_intro(screen, clock, uicontroller.audioplayer)
        do_scroll(screen, clock, gameData.categories)
        pygame.mouse.set_visible(1)

    # Prep for primary loop
    pygame.event.set_allowed(None)
    pygame.event.set_allowed(EVENTS_ALLOWED)
    uicontroller.draw(screen)

    # Primary loop
    while not gs.state == gs.GAME_END:
        # Events
        handle_events(gs, gameData, uicontroller)
        handle_buzzers(gs, gameData)
        
        if gs.state == gs.QUIT:
            pygame.quit()
            sys.exit()

        # Update
        gameData.update(gs)
        uicontroller.update(gs, gameData)
        gs.transition_state_immediate_linear(gameData)
        transition_state_branching(gs, gameData, uicontroller)

        # Draw
        uicontroller.draw(screen)

        # Cleanup
        pygame.event.pump()
        clock.tick_busy_loop(FPS_LIMIT)

    # Post game: Congratulations screen and credits
    pygame.mouse.set_visible(0)
    do_congrats(screen, clock, gameData.winners, uicontroller.audioplayer)
    do_credits(screen, clock, uicontroller.audioplayer, FPS_LIMIT)
Exemple #3
0
def main(*flags):
    """Main game loop and event handling."""

    # Initialization
    pygame.init()
    #Put window in center of screen
    os.environ['SDL_VIDEO_WINDOW_POS'] = 'center'

    screen = pygame.display.set_mode(SCREEN_SIZE,
                                     pygame.FULLSCREEN if FULLSCREEN else 0)
    pygame.display.set_caption('Jeopardy!')

    # Declarations
    gameData = GameData()
    gs = JeopGameState()
    uicontroller = Controller(screen, gameData, FPS_LIMIT)
    clock = pygame.time.Clock()

    # Intro sequence (control passed completely to functions)
    if SKIP_INTRO_FLAG not in flags:
        pygame.mouse.set_visible(0)
        do_intro(screen, clock, uicontroller.audioplayer)
        do_scroll(screen, clock, gameData.categories)
        pygame.mouse.set_visible(1)

    # Prep for primary loop
    pygame.event.set_allowed(None)
    pygame.event.set_allowed(EVENTS_ALLOWED)
    uicontroller.draw(screen)

    # Primary loop
    while not gs.state == gs.GAME_END:
        # Events
        handle_events(gs, gameData, uicontroller)
        handle_buzzers(gs, gameData)

        if gs.state == gs.QUIT:
            pygame.quit()
            sys.exit()

        # Update
        gameData.update(gs)
        uicontroller.update(gs, gameData)
        gs.transition_state_immediate_linear(gameData)
        transition_state_branching(gs, gameData, uicontroller)

        # Draw
        uicontroller.draw(screen)

        # Cleanup
        pygame.event.pump()
        clock.tick_busy_loop(FPS_LIMIT)

    # Post game: Congratulations screen and credits
    pygame.mouse.set_visible(0)
    do_congrats(screen, clock, gameData.winners, uicontroller.audioplayer)
    do_credits(screen, clock, uicontroller.audioplayer, FPS_LIMIT)
Exemple #4
0
def main(debug, hostname, port, name):
    if debug:
        logging.basicConfig(format='[%(asctime)s] %(message)s', level=logging.DEBUG)
    else:
        logging.basicConfig(format='[%(asctime)s] %(message)s', level=logging.INFO)

    # Connect to game server
    GameServer = ServerComms(hostname, port)

    # Spawn our tank
    logging.info("Creating tank with name '{}'".format(name))
    GameServer.sendMessage(ServerMessageTypes.CREATETANK, {'Name': name})

    # static reference to game, easier for other files to use it
    game.data = GameData(GameServer, name)

    game.data.game_server.sendMessage(ServerMessageTypes.TOGGLEFORWARD)
    # state.game_server.sendMessage(ServerMessageTypes.MOVEFORWARDDISTANCE, {"Amount": 10})

    behavior_tree: BehaviorTree = behavior_tree_factory.get_tree()

    while True:
        messagePayload = GameServer.readMessage()
        game.data.update(messagePayload)

        behavior_tree.run()
Exemple #5
0
    def __init__(self, showbase):
        self.showbase = showbase

        self.ready = False

        self.background = DirectFrame(
            frameSize=(-1, 1, -1, 1),
            frameTexture='media/gui/mainmenu/menu.png',
            parent=self.showbase.render2d,
        )

        self.title = OnscreenText(text='Lobby!',
                                  fg=(1, 1, 1, 1),
                                  parent=self.background,
                                  pos=(-0.6, 0.1),
                                  scale=0.06)

        self.buttons = []
        controlButtons = Vec3(-0.60, 0, -0.79)
        # Toggle ready
        p = controlButtons + Vec3(-0.25, 0, 0)
        self.toggleReadyButton = DirectButton(
            text='Ready/Unready',
            pos=p,
            scale=0.048,
            relief=DGG.GROOVE,
            command=self.toggleReady,
        )
        self.buttons.append(self.toggleReadyButton)
        # Disconnect
        p = controlButtons + Vec3(0.0, 0.0, 0.0)
        self.disconnectButton = DirectButton(
            text='Disconnect',
            pos=p,
            scale=0.048,
            relief=DGG.GROOVE,
            command=self.disconnect,
        )
        self.buttons.append(self.disconnectButton)

        # Send message
        p = controlButtons + Vec3(0.25, 0.0, 0.0)
        self.sendMessageButton = DirectButton(
            text='Send Message',
            pos=p,
            scale=0.048,
            relief=DGG.GROOVE,
            command=self.sendMessage,
            extraArgs=[''],
        )
        self.buttons.append(self.sendMessageButton)
        # Message input
        self.message = DirectEntry(
            command=self.sendMessage,
            focusInCommand=self.clearText,
            frameSize=(-3, 3, -.5, 1),
            initialText='',
            parent=self.buttons[2],
            pos=(0, -0.6, -1.5),
            text_align=TextNode.ACenter,
        )

        self.showbase.gameData = GameData()

        self.showbase.users = []

        self.hide()
Exemple #6
0
class Server(ShowBase):
	def __init__(self):
		ShowBase.__init__(self)

		# Server Networking handling stuff
		self.compress = False

		self.cManager = QueuedConnectionManager()
		self.cListener = QueuedConnectionListener(self.cManager, 0)
		self.cReader = QueuedConnectionReader(self.cManager, 0)
		self.cWriter = ConnectionWriter(self.cManager, 0)

		self.tempConnections = []
		self.unauthenticatedUsers = []
		self.users = []

		self.passedData = []

		self.connect(9099, 1000)
		self.startPolling()

		self.attemptAuthentication()
		
		self.taskMgr.doMethodLater(0.5, self.lobbyLoop, 'Lobby Loop')

	def connect(self, port, backlog = 1000):
		# Bind to our socket
		tcpSocket = self.cManager.openTCPServerRendezvous(port, backlog)
		self.cListener.addConnection(tcpSocket)

	def startPolling(self):
		self.taskMgr.add(self.tskListenerPolling, "serverListenTask", -40)
		self.taskMgr.add(self.tskDisconnectPolling, "serverDisconnectTask", -39)

	def tskListenerPolling(self, task):
		if self.cListener.newConnectionAvailable():
			rendezvous = PointerToConnection()
			netAddress = NetAddress()
			newConnection = PointerToConnection()

			if self.cListener.getNewConnection(rendezvous, netAddress, newConnection):
				newConnection = newConnection.p()
				newConnection.setNoDelay(True)
				self.tempConnections.append(newConnection)	# Remember connection
				self.cReader.addConnection(newConnection)	# Begin reading connection
		return Task.cont

	def tskDisconnectPolling(self, task):
		while self.cManager.resetConnectionAvailable() == True:
			connPointer = PointerToConnection()
			self.cManager.getResetConnection(connPointer)
			connection = connPointer.p()
			
			# Remove the connection we just found to be "reset" or "disconnected"
			self.cReader.removeConnection(connection)
			
			# remove from our activeConnections list
			if connection in self.tempConnections:
				self.tempConnections.remove(connection)
			for user in self.unauthenticatedUsers:
				if connection == user.connection:
					self.unauthenticatedUsers.remove(user)
			for user in self.users:
				if connection == user.connection:
					user.connection = None
					self.passData(('disconnect', user.name), None)
		
		return Task.cont

	def broadcastData(self, data):
		# Broadcast data out to all users
		for user in self.users:
			if user.connection:
				self.sendData(data, user.connection)

	def processData(self, netDatagram):
		myIterator = PyDatagramIterator(netDatagram)
		return self.decode(myIterator.getString())

	def getUsers(self):
		# return a list of all users
		return self.users

	def encode(self, data, compress = False):
		# encode(and possibly compress) the data with rencode
		return rencode.dumps(data, compress)

	def decode(self, data):
		# decode(and possibly decompress) the data with rencode
		return rencode.loads(data)

	def sendData(self, data, con):
		myPyDatagram = PyDatagram()
		myPyDatagram.addString(self.encode(data, self.compress))
		self.cWriter.send(myPyDatagram, con)

	def passData(self, data, connection):
		self.passedData.append((data, connection))

	def getData(self):
		data = []
		for passed in self.passedData:
			data.append(passed)
			self.passedData.remove(passed)
		while self.cReader.dataAvailable():
			datagram = NetDatagram()
			if self.cReader.getData(datagram):
				if datagram.getConnection() in self.tempConnections:
					self.processTempConnection(datagram)
					continue
				for authed in self.users:
					if datagram.getConnection() == authed.connection:
						data.append((self.processData(datagram), datagram.getConnection()))
		return data

	def processTempConnection(self, datagram):
		connection = datagram.getConnection()
		package = self.processData(datagram)
		if len(package) == 2:
			if package[0] == 'username':
				print 'attempting to authenticate', package[1]
				self.tempConnections.remove(connection)
				if not self.online:
					user = User(package[1], connection)
					# confirm authorization
					self.sendData(('auth', user.name), user.connection)
					self.updateClient(user)
					self.users.append(user)
				else:
					self.client.sendData(('auth', package[1]))
					self.unauthenticatedUsers.append(User(package[1], connection))

	def attemptAuthentication(self):
		config = ConfigParser.RawConfigParser()
		config.read('server.cfg')
		self.SERVER_NAME = config.get('SERVER DETAILS', 'serverName')

		config = ConfigParser.RawConfigParser()
		config.read('master.cfg')
		self.LOGIN_IP = config.get('MASTER SERVER CONNECTION', 'masterIp')
		self.LOGIN_PORT = config.getint('MASTER SERVER CONNECTION', 'masterPort')

		# Client for connecting to main server for showing exists and receiving clients
		self.client = Client(self, self.LOGIN_IP, self.LOGIN_PORT, compress = True)
		if self.client.getConnected():
			self.client.sendData(('server', self.SERVER_NAME))
			self.taskMgr.add(self.clientValidator, 'Client Validator')
			self.client.sendData(('state', 'lobby'))
			self.online = True
		else:
			# client not connected to login/auth server
			print 'Could not connect to Authentication Server.'
			print 'Server is not in online mode. No Authentication will happen for clients.'
			print 'Restart Server to attempt to connect to Authentication Server.'
			self.client = None
			self.online = False

	def clientValidator(self, task):
		temp = self.client.getData()
		for package in temp:
			if len(package) == 2:
				if package[0] == 'auth':
					print 'User authenticated: ', package[1]
					for user in self.unauthenticatedUsers:
						if user.name == package[1]:
							# confirm authorization
							self.sendData(('auth', user.name), user.connection)
							# send all required data to user
							self.updateClient(user)
							# all authenticated users
							self.users.append(user)
							self.unauthenticatedUsers.remove(user)
							print 'confirmation sent to ', package[1]
							break
				elif package[0] == 'fail':
					print 'User failed authentication: ', package[1]
					for user in self.unauthenticatedUsers:
						if user.name == package[1]:
							self.sendData(('fail', user.name), user.connection)
							self.unauthenticatedUsers.remove(user)
							break
		return task.again

	def updateClient(self, user):
		for existing in self.users:
			if existing.name != user.name:
				self.sendData(('client', existing.name), user.connection)
				self.sendData(('ready', (existing.name, existing.ready)), user.connection)
				if existing.connection:
					self.sendData(('client', user.name), existing.connection)
		self.sendData(('client', user.name), user.connection)

	def lobbyLoop(self, task):
		temp = self.getData()
		for package in temp:
			if len(package) == 2:
				print "Received: ", str(package)
				packet = package[0]
				connection = package[1]
				if len(packet) == 2:
					# check to make sure connection has username
					for user in self.users:
						if user.connection == connection:
							# if chat packet
							if packet[0] == 'chat':
								print 'Chat: ', packet[1]
								# Broadcast data to all clients ("username: message")
								self.broadcastData(('chat', (user.name, packet[1])))
							# else if ready packet
							elif packet[0] == 'ready':
								print user.name, ' changed readyness!'
								user.ready = packet[1]
								self.broadcastData(('ready', (user.name, user.ready)))
							# else if disconnect packet
							elif packet[0] == 'disconnect':
								print user.name, ' is disconnecting!'
								self.users.remove(user)
								self.broadcastData(('disconnect', user.name))
							# break out of for loop
							break
		# if all players are ready and there is X of them
		gameReady = True
		# if there is any clients connected
		if self.getUsers() == []:
			gameReady = False
		for user in self.users:
			if not user.ready:
				gameReady = False
		if gameReady:
			self.prepareGame()
			return task.done
		return task.again
		
	def prepareGame(self):
		if self.camera:
			# Disable Mouse Control for camera
			self.disableMouse()
			
			self.camera.setPos(0, 0, 500)
			self.camera.lookAt(0, 0, 0)

		self.gameData = GameData(True)
		
		# game data
		self.broadcastData(('gamedata', self.gameData.packageData()))
		self.broadcastData(('state', 'preround'))
		if self.online:
			self.client.sendData(('state', 'preround'))
		print "Preparing Game"
		self.gameTime = 0
		self.tick = 0

		usersData = []
		for user in self.users:
			usersData.append(user.gameData)
		self.game = Game(self, usersData, self.gameData)
		self.taskMgr.doMethodLater(0.5, self.roundReadyLoop, 'Game Loop')
		print "Round ready State"
		
	def roundReadyLoop(self, task):
		temp = self.getData()
		for package in temp:
			if len(package) == 2:
				print "Received: ", str(package)
				if len(package[0]) == 2:
					for user in self.users:
						if user.connection == package[1]:
							if package[0][0] == 'round':
								if package[0][1] == 'sync':
									user.sync = True
		# if all players are ready and there is X of them
		roundReady = True
		# if there is any clients connected
		for user in self.users:
			if user.sync == False:
				roundReady = False
		if roundReady:
			self.taskMgr.doMethodLater(2.5, self.gameLoop, 'Game Loop')
			print "Game State"
			return task.done
		return task.again
	
	def gameLoop(self, task):
		# process incoming packages
		temp = self.getData()
		for package in temp:
			if len(package) == 2:
				# check to make sure connection has username
				for user in self.users:
					if user.connection == package[1]:
						user.gameData.processUpdatePacket(package[0])
		# get frame delta time
		dt = self.taskMgr.globalClock.getDt()
		self.gameTime += dt
		# if time is less than 3 secs (countdown for determining pings of clients?)
		# tick out for clients
		while self.gameTime > gameTick:
			# update all clients with new info before saying tick
			for user in self.users:
				updates = user.gameData.makeUpdatePackets()
				for packet in updates:
					self.broadcastData((user.name, packet))
			self.broadcastData(('tick', self.tick))
			self.gameTime -= gameTick
			self.tick += 1
			# run simulation
			if not self.game.runTick(gameTick):
				print 'Game Over'
				# send to all players that game is over (they know already but whatever)
				# and send final game data/scores/etc
				for user in self.users:
					user.ready = False
				self.taskMgr.doMethodLater(0.5, self.lobbyLoop, 'Lobby Loop')
				return task.done
		return task.cont
Exemple #7
0
 def load_realtime_data(self):
     sharedData = {}
     self.parser = self.bot.set_up_parser(sharedData, self.do_turn)
     self.data = GameData(sharedData)
     self.data.reset()
Exemple #8
0
class Brain:
    """The brain: parses lines, combines data classes to make decisions"""
    def __init__(self, bot):
        with Timer() as t:
            self.bot = bot
            self.load_realtime_data()
            self.load_precalc_data()
            self.iterations = 400
        self.bot.log("Brain started up in {t} secs".format(t=t.secs))

    def load_realtime_data(self):
        sharedData = {}
        self.parser = self.bot.set_up_parser(sharedData, self.do_turn)
        self.data = GameData(sharedData)
        self.data.reset()

    def load_precalc_data(self):
        """Loads pre-computed hand data"""
        infile = os.path.join('data', 'preflop_wins_5000.pickle')
        try:
            in_stream = open(infile, 'r')
            try:
                self.preflop_equity = pickle.load(in_stream)
                self.bot.log("Loaded preflop equity file")
            finally:
                in_stream.close()
        except IOError:
            self.bot.log("IO error loading {f}".format(f=infile))

    def parse_line(self, line):
        """Feeds a line to the parsers"""
        success = self.parser.handle_line(line)
        if success:
            self.data.update()
        else:
            self.bot.log("didn't handle line: " + line + "\n")

    def pot_odds(self):
        """Return the pot odds, or how much we need to gain to call"""
        to_call = self.data.sidepot
        pot_total = to_call + self.data.pot
        return MathUtils.percentage(to_call, pot_total)

    def do_turn(self, timeLeft_ms):
        """Wraps internal __do_turn so we can time how long each turn takes"""
        with Timer() as t:
            self.__do_turn(timeLeft_ms)
        self.bot.log("Finished turn in {t}s ({i} sims), had {l}s remaining"
                     .format(t=t.secs, i=self.iterations,
                             l=(timeLeft_ms/1000 - t.secs)))

    def __do_turn(self, timeLeft_ms):
        """Callback for when the brain has to make a decision"""
        if not self.data.hand:
            self.bot.log("No hand, killing ourselves. Data={d}"
                         .format(d=self.data))
            self.bot.fold()
            return

        hand = self.data.hand
        pot_odds = self.pot_odds()
        equity = 0

        if not self.data.table_cards:
            equity = self.preflop_equity[repr(hand)]
        else:
            simulator = HandSimulator(hand, self.data.table_cards)
            best_hand, score = simulator.best_hand()
            self.bot.log("best 5: {b} score: {s}"
                         .format(b=str(best_hand), s=score))
            equity = simulator.simulate(self.iterations)

        self.bot.log("{h}, equity: {e}%, pot odds: {p}%"
                     .format(h=hand, e=equity, p=pot_odds))

        self.pick_action(equity, pot_odds)

    def pick_action(self, equity, pot_odds):
        """Look at our expected return and do something.
        Will be a semi-random mix of betting, folding, calling"""
        to_call = self.data.sidepot
        # action to us: check or bet
        if to_call == 0:
            if equity > 0.7 or self.r_test(0.03):
                self.bot.bet(self.big_raise())
            elif equity > 0.5 or self.r_test(0.05):
                self.bot.minimum_bet()
            else:  # equity <= 0.3:
                self.bot.check()

        # use pot odds to call/bet/fold
        else:
            return_ratio = equity / pot_odds
            if return_ratio > 1.5 or self.r_test(0.02):
                self.bot.bet(self.big_raise())
            elif return_ratio > 1:
                self.bot.call(to_call)
            else:
                self.bot.fold()

    def big_raise(self):
        """Returns a big raise, 30-50% of the pot"""
        pot = self.data.pot
        bet_raise = random.uniform(0.3, 0.5) * pot
        self.bot.log("big raise of {r}".format(r=bet_raise))
        return bet_raise

    def minimum_bet(self):
        """Returns a minimum bet, 2.5-4 BB"""
        bet = self.data.big_blind * random.uniform(2, 4)
        self.bot.log("min bet of {b}".format(b=bet))
        return bet

    def r_test(self, fraction):
        """Given a number [0,1], randomly return true / false
        s.t. r_test(0.5) is true ~50% of the time"""
        passed = random.uniform(0, 1) < fraction
        if passed:
            self.bot.log("r_test() passed for %{f}".format(f=fraction))
        return passed
Exemple #9
0
def main():
    parse_args() # Sets CONFIG options.
                 # Must be called before UIManager instantiated.
    
    pygame.init()
    pygame.display.set_caption('Flippyflap Wivs')
    environ['SDL_VIDEO_WINDOW_POS'] = 'center'

    # Load high score
    pd = PersistentData(DATA_PATH)
    if path.exists(DATA_PATH):
        pd.load()
    else:
        pd.high_score = 0
    
    clock = pygame.time.Clock()
    dt = 1
    gdt = 60 / CONFIG.FPS_LIMIT
    gs = GameState()
    gd = GameData(pd.high_score)
    uim = UIManager(gd.n_columns, gdt*gd.scroll_speed)
    end = False

    # Prep for game loop
    pygame.mouse.set_visible(0)
    pygame.event.set_allowed(None)
    pygame.event.set_allowed(
        (pygame.KEYDOWN, pygame.QUIT, pygame.MOUSEBUTTONDOWN)
    )

    # Game loop
    while not end:
        # Transition state
        gs.transition_state()
        
        # Handle Events
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                end = True
            elif event.type == pygame.KEYDOWN:
                handle_event_keydown(gs, event)
            elif event.type == pygame.MOUSEBUTTONDOWN:
                handle_event_mousebuttondown(gs, event)

        if gs.state == gs.QUIT:
            end = True

        if gs.state != gs.PAUSE:
            # Update
            gd.update(gs, gdt)
            uim.update(gs, gd, dt)
            gd.postupdate(gs, gdt)

            # Draw
            uim.draw()

        # Cleanup
        clock.tick(CONFIG.FPS_LIMIT)
        fps = clock.get_fps()
        gdt = 60 / (fps if fps > 15 else CONFIG.FPS_LIMIT)                                       
        dt = fps / CONFIG.FPS_LIMIT
        pygame.event.pump()


    # Save high score
    pd.high_score = gd.high_score
    pd.save()
    pygame.quit()
Exemple #10
0
 def load_realtime_data(self):
     sharedData = {}
     self.parser = self.bot.set_up_parser(sharedData, self.do_turn)
     self.data = GameData(sharedData)
     self.data.reset()
Exemple #11
0
class Brain(BetSizeCalculator, Fear):
    """The brain: parses lines, combines data classes to make decisions"""
    def __init__(self, bot):
        with Timer() as t:
            self.bot = bot
            self.load_realtime_data()
            self.load_precalc_data()
            self.iterations = 2000
        self.bot.log("Brain started up in {t} secs".format(t=t.secs))

    def load_realtime_data(self):
        sharedData = {}
        self.parser = self.bot.set_up_parser(sharedData, self.do_turn)
        self.data = GameData(sharedData)
        self.data.reset()

    def load_precalc_data(self):
        """Loads pre-computed hand data"""
        preflop = preflop_equity.PreflopEquity(log_func=self.bot.log)
        self.preflop_equity = preflop.data

    def parse_line(self, line):
        """Feeds a line to the parsers"""
        success = self.parser.handle_line(line)
        if success:
            self.data.update()
        else:
            self.bot.log("didn't handle line: '{}'".format(line))

    def do_turn(self, bot, total_time_left_ms):
        """Wraps internal __do_turn so we can time how long each turn takes"""
        time_left = min(total_time_left_ms, self.data.time_per_move)
        if not bot or bot != self.data.me:
            return
        with Timer() as t:
            self.__do_turn(time_left)
        if not self.data.table_cards:
            turn_type = "preflop"
        else:
            turn_type = "{} sims".format(self.iterations)
        left = (time_left / 1000) - t.secs
        self.bot.log("Finished turn in {t}s ({s}), had {l}s remaining"
                     .format(t=t.secs, s=turn_type, l=left))

    def __do_turn(self, time_left_ms):
        """Callback for when the brain has to make a decision"""
        if not self.data.hand:
            self.bot.log("No hand, killing ourselves. Data={d}"
                         .format(d=self.data))
            self.bot.fold()
            return

        hand = self.data.hand
        stack = self.our_stack()
        to_call = self.to_call(silent=False)
        pot_odds = self.pot_odds()
        equity = 0
        self.update_fear(to_call)
        preflop_fear = self.data.preflop_fear
        hand_fear = self.data.hand_fear

        # preflop, no big raises. safe to use our precalculated win %
        if not self.data.table_cards and preflop_fear == -1:
            equity = self.preflop_equity[hand.simple()]
            source = "preflop"
        else:
            simulator = HandSimulator(hand, self.data.table_cards,
                                      self.preflop_equity)
            best_hand, score = simulator.best_hand()
            equity = self.__run_simulator(simulator, time_left_ms,
                                          preflop_fear, hand_fear)
            source = "sim"

        self.bot.log(" hand: {h}, table: {t}"
                     .format(h=hand, t=[str(t) for t in self.data.table_cards]))
        if self.data.table_cards:
            self.bot.log(" best 5: {b} score: {s}"
                         .format(b=[str(c) for c in best_hand], s=str(score)))
        self.bot.log(" win: {e:.2f}% ({s}), pot odds: {p:.2f}%, stack={m}"
                     .format(e=equity, s=source, p=pot_odds, m=stack))
        self.bot.log(" pre-fear={pf}, hand-fear=({hf})"
                     .format(pf=preflop_fear, hf=hand_fear))

        self.pick_action(equity, to_call, pot_odds)

    def __run_simulator(self, simulator, time_left_ms, hand_filter, hand_fear):
        results = []
        step_size = 100
        start_time = time.clock() * 1000
        end_time = start_time + time_left_ms - 50
        for i in range(0, self.iterations, step_size):
            now = time.clock() * 1000
            if now >= end_time:
                self.bot.log(" stopping simulation after {} runs".format(i))
                break
            equity = simulator.simulate(step_size, hand_filter, hand_fear)
            results.append(equity)
        return sum(results) / len(results)

    def pick_action(self, equity, to_call, pot_odds):
        """Look at our expected return and do something.
        Will be a semi-random mix of betting, folding, calling"""
        # action to us: check or bet
        if to_call == 0:
            # lock hands - 1/3 of the time make a small bet instead of a big one
            if equity > 90 and self.r_test(0.33, 'lock_trap'):
                self.make_bet(self.minimum_bet("trap1"))
            elif equity > 65 or (equity > 40 and self.r_test(0.03, 'c1')):
                self.make_bet(self.big_raise("R1"))
            elif equity > 55 or self.r_test(0.02, 'c2'):
                self.make_bet(self.minimum_bet("R2"))
            else:
                self.bot.check()
        # TODO: combine these and make them aware of button
        # use pot odds to call/bet/fold
        else:
            return_ratio = equity / pot_odds
            self.bot.log(" return ratio={:.3f}".format(return_ratio))
            if equity > 70 or (equity > 40 and self.r_test(0.03, 'po1')):
                self.make_bet(self.big_raise("R3"))
            elif to_call < self.data.big_blind and \
               (equity > 55 or self.r_test(0.03, 'po2')):
                # small preflop raise from SB, get more money into the pot
                self.make_bet(self.minimum_bet("R4"))
            elif return_ratio > 1.25:
                self.bot.log(" return ratio > 1.25, calling {}".format(to_call))
                self.bot.call(to_call)
            elif return_ratio > 1 \
              and MathUtils.percentage(to_call, self.our_stack()) < 10:
                self.bot.log(" return ratio > 1 and bet is small, calling {}"
                             .format(to_call))
                self.bot.call(to_call)
            else:
                self.bot.fold()

    def make_bet(self, amount):
        """Make a bet, also update fear assumming the opponent will call."""
        self.update_fear(amount)
        self.bot.bet(amount)

    def r_test(self, fraction, block=None):
        """Given a number [0,1], randomly return true / false
        s.t. r_test(0.5) is true ~50% of the time"""
        passed = random.uniform(0, 1) < fraction
        if passed:
            self.bot.log(" r_test({f}%) passed from {b}"
                         .format(f=100*fraction, b=block))
        return passed