Beispiel #1
0
    def __init__(self, coordinatorId, playerList, costList, gameFreq, gameType, playerItTh, plot_results=False):
        """
        Create a power game object

        Keyword arguments:
        coordinatorId -- Numerical cluster id.
        playerList -- List of node ids  representing players, e.g [51,23,31,47] two player game
                      with player 1 51->23 (tx node 51, rx node 23), player 2 31->47.
        costList -- List of player costs.
        gameFreq -- Frequency used for transmitting and sensing
        gameType -- Type of game played
        playerItTh: player iterations
        """
        self.coordId = coordinatorId
        self.nodesIdList = playerList
        self.playerCostList = costList
        self.gameFreq = gameFreq
        self.gameType = gameType
        self.playerIterationsThreshold = playerItTh
        self.plot_results = plot_results

        self.players = dict()
        self.equilibrium = dict()
        self.playersCrtTxPovers = dict()
        self.directGains = dict()
        self.crossGains = dict()
        self.crossGainsTotalInterf = dict()
        self.playerEvent = dict()
        self.useDummyData = False

        if self.plot_results:
            self.my_plot = GameLivePlot('Dynamic (cost adaptive) power allocation game')
Beispiel #2
0
class PowerGame():
    """
	Power allocation game implementation for LOG-a-TEC testbed.

	Game type:
	1 - game step for experiment 3
	2 -
	3 - multiple run experiments, measure sum(h_ji*p_j)+n_0 individually
	4 - multiple run experiments, measure sum(h_ji*p_j)+n_0 when all opponents transmit
	5 - dynamic game, i.e. players come and go
	6 - run the game for a number of iterations, perform channel measurements at specified periods
	"""
    
    # number of game iterations
    nr_game_iterations = 1
    # define period, in game iterations, for channel measurements
    measuring_period = 10

    # list of players playing the game
    players = list()

    # dictionary used to store players transmitting powers
    playersCrtTxPovers = dict()

    # store direct and cross gains
    directGains = dict()
    crossGains = dict()
    crossGainsTotalInterf = dict()

    # store equilibrium state for each player
    equilibrium = dict()
    playerEvent = dict()

    dummyDirectGains = dict()
    dummyCrossGains = dict()
    dummyCrossGainsTotalInterf = dict()
    dummyTxPowers = dict()
    useDummyData = False

    # used to avoid infinite loops
    playerIterationsThreshold = 0

    def __init__(self, coordinatorId, playerList, costList, gameFreq, gameType, playerItTh, plot_results=False):
        """
        Create a power game object

        Keyword arguments:
        coordinatorId -- Numerical cluster id.
        playerList -- List of node ids  representing players, e.g [51,23,31,47] two player game
                      with player 1 51->23 (tx node 51, rx node 23), player 2 31->47.
        costList -- List of player costs.
        gameFreq -- Frequency used for transmitting and sensing
        gameType -- Type of game played
        playerItTh: player iterations
        """
        self.coordId = coordinatorId
        self.nodesIdList = playerList
        self.playerCostList = costList
        self.gameFreq = gameFreq
        self.gameType = gameType
        self.playerIterationsThreshold = playerItTh
        self.plot_results = plot_results

        self.players = dict()
        self.equilibrium = dict()
        self.playersCrtTxPovers = dict()
        self.directGains = dict()
        self.crossGains = dict()
        self.crossGainsTotalInterf = dict()
        self.playerEvent = dict()
        self.useDummyData = False

        if self.plot_results:
            self.my_plot = GameLivePlot('Dynamic (cost adaptive) power allocation game')

    def set_total_game_iterations(self, new_game_iter):
        """
        set the number of game iterations.
        :param new_game_iter: number of iterations for the power allocation game
        :return:
        """
        self.nr_game_iterations = new_game_iter

    def set_measuring_period(self, new_period):
        """
        set the measuring period
        :param new_period:
        :return:
        """
        self.measuring_period = new_period


    def initPlayers(self):
        """Just initialize player objects and check if parameters are ok"""
        self.nrPlayers = len(self.nodesIdList) / 2

        if self.nrPlayers != len(self.playerCostList):
            print "Number of players and costs given do not match!"
            return

        for i in range(0, len(self.nodesIdList), 2):
            self.players[i / 2] = GamePlayer(self.coordId, self.nodesIdList[i], self.nodesIdList[i + 1],
                                             self.playerCostList[i / 2], i / 2, self.gameFreq, self.gameType)
            # get initial tx powers, these powers are random choices from available powers
            self.playersCrtTxPovers[i / 2] = self.players[i / 2].physicalLayer.getCrtTxPower()
            self.equilibrium[i / 2] = False
            self.playerEvent[i / 2] = False

            # test if initialization was successful
            # INITIALIZATION WAS SUCCESFUL
            # for p in self.players:
            # p.physicalLayer.transmitObject.printPlayerTxLayerInfo()
            # p.physicalLayer.senseObject.printPlayerSenseLayerInfo()
            # PLAYERS CAN TRANSMIT
            # self.players[0].physicalLayer.transmitObject.sendData(4)

            # for i in self.playersCrtTxPovers.keys():
            # print "Player %d transmits with %d dBm"%(i,self.playersCrtTxPovers[i])

        # initi plot
        if self.plot_results:
            self.my_plot.init_plot(self.nrPlayers)


    def setGameDummyData(self, dDirectG, dCrossG, dCrossTotG, dictTxPowers=None):
        self.useDummyData = True
        self.dummyDirectGains = dDirectG
        self.dummyCrossGains = dCrossG
        self.dummyCrossGainsTotalInterf = dCrossTotG
        self.setDummiData(self.dummyDirectGains, self.dummyCrossGains, self.crossGainsTotalInterf)
        if dictTxPowers is not None:
            self.dummyTxPowers = dictTxPowers
            for key in dictTxPowers:
                self.playersCrtTxPovers[key] = dictTxPowers[key]
                self.players[key].physicalLayer.changeTxPower(dictTxPowers[key])

    def resetGame(self):
        """Use this when the game is played multiple times."""
        self.resetLogicalDictionary(self.playerEvent, False)
        self.resetLogicalDictionary(self.equilibrium, False)
        for playerId in self.players:
            self.players[playerId].resetPlayerObject()
            self.playersCrtTxPovers[playerId] = self.players[playerId].physicalLayer.getCrtTxPower()
        # need to measure gains and cross gains and scale player costs accordingly
        if self.useDummyData:
            # if self.gameType == 4:
            # self.dummyCrossGainsTotalInterf
            self.setDummiData(self.dummyDirectGains, self.dummyCrossGains, self.dummyCrossGainsTotalInterf)
            # adaugat doar pentru teste cu variatii
            # for key in self.directGains:
        # self.directGains[key] = self.directGains[key] + random.gauss(0,1)*1e-6
        # for key in self.crossGains:
        # for k in self.crossGains[key]:
        # self.crossGains[key][k] = self.crossGains[key][k] + random.gauss(0,1)*1e-6
        else:
            if self.gameType == 4:
                self.measureGainsV2()
            else:
                self.measureGains()
        self.scaleCosts()

    def measureGains(self):
        """Measure h_ii and h_ji between players, store these values in a list and a dictionary"""

        # txNodes = list()
        # rxNodes = list()

        txPlayers = list()
        rxPlayers = list()

        # measure direct gains
        for p in self.players:
            # get tx and rx nodes, used for cross gain
            txPlayers.append(self.players[p])
            rxPlayers.append(self.players[p])

            self.directGains[self.players[p].getPlayerNumber()] = measureGainBetwTxRx(self.coordId, self.players[
                p].physicalLayer.txNode, self.players[p].physicalLayer.rxNode, self.gameFreq, txPower=0,
                                                                                      transDuration=6)
            self.players[p].physicalLayer.setDirectGain(self.directGains[self.players[p].getPlayerNumber()])
            # wait for things to cool down
            time.sleep(2)

        # measure cross gains
        for p in self.players:
            result = dict()
            aux1 = txPlayers[:]
            aux1.remove(self.players[p])

            for n in aux1:
                result[n.getPlayerNumber()] = measureGainBetwTxRx(self.coordId, n.physicalLayer.txNode,
                                                                  self.players[p].physicalLayer.rxNode, self.gameFreq,
                                                                  txPower=0, transDuration=6)
                # wait for things to cool down
                time.sleep(2)
            # add cross gains to dictionary
            self.crossGains[p] = result

        print self.directGains
        for key in self.crossGains.keys():
            print self.crossGains[key]

    def measureGainsV2(self):
        """Measure h_ii and total h_ji between players, store these values in list and dictionary"""
        # txPlayers = list()
        # rxPlayers = list()
        vesnaNodes = list()
        # direct gains
        for p in self.players:
            # get tx and rx nodes for cross gain
            # txPlayers.append(self.players[p])
            # rxPlayers.append(self.players[p])
            vesnaNodes.append(self.players[p].physicalLayer.txNode)
            vesnaNodes.append(self.players[p].physicalLayer.rxNode)

        dirG = getDirectGainsNPlVers2(self.coordId, vesnaNodes)
        crG = getCrossGainsNPlVers2(self.coordId, vesnaNodes)

        index = 0
        for p in self.players:
            self.directGains[p] = dirG[index]
            self.players[p].physicalLayer.setDirectGain(dirG[index])
            self.crossGainsTotalInterf[p] = crG[index]
            index += 1

    def setDummiData(self, dummyDirect, dummyCross, dummyTotalCross=None, gameT=1):
        """Just set some old measured data and skip the measuring part"""
        self.directGains = dummyDirect
        if gameT == 4 and dummyTotalCross is not None:
            self.crossGainsTotalInterf = dummyTotalCross
        else:
            self.crossGains = dummyCross
        for key in dummyDirect:
            self.players[key].physicalLayer.setDirectGain(dummyDirect[key])
        if self.dummyTxPowers is not None:
            for key in self.dummyTxPowers:
                self.playersCrtTxPovers[key] = self.dummyTxPowers[key]
                self.players[key].physicalLayer.changeTxPower(self.dummyTxPowers[key])

    def scaleCosts(self):
        """
        Scale player cost such that best response is in interval [-30,0] dBm.
        Return True if operation is successful.
        """
        # check if c_i is in [0,1]
        for x in self.playerCostList:
            if x < 0.0 or x > 1.0:
                print "costs must be in interval [0,1]"
                print "c_i = %.2f" % (x)
                return False

        # scale costs based on direct and cross gains
        for plId in self.players:
            if self.gameType == 4:
                self.scaleCostForPlayerV2(plId)
            else:
                self.scaleCostForPlayer(plId)

        return True

    def scaleCostForPlayerWithMask(self, playerId, mask):
        mySum = 0
        for crossGj in self.crossGains[playerId]:
            if mask[crossGj]:
                mySum += self.crossGains[playerId][crossGj] * math.pow(10.00, float(
                    self.playersCrtTxPovers[crossGj]) / 10.00) * 1e-3
        omega = mySum / float(self.directGains[playerId])
        newCost = 1 / (float(1e-3 + omega)) + self.players[playerId].getPlayerCost() * (
            1 / (float(1e-6 + omega)) - 1 / (float(1e-3 + omega)))
        self.players[playerId].setScaledCost(newCost)

    def scaleCostForPlayer(self, playerId):
        """
        Scale player cost such that best response is in interval [-30,0] dBm.

        Keyword arguments:
        playerId -- numerical id identifying player.
        """
        mySum = 0
        for crossGj in self.crossGains[playerId]:
            mySum += self.crossGains[playerId][crossGj] * math.pow(10.00, float(
                self.playersCrtTxPovers[crossGj]) / 10.00) * 1e-3
        omega = mySum / float(self.directGains[playerId])
        newCost = 1 / (float(1e-3 + omega)) + self.players[playerId].getPlayerCost() * (
            1 / (float(1e-6 + omega)) - 1 / (float(1e-3 + omega)))
        self.players[playerId].setScaledCost(newCost)

    def scaleCostForPlayerV2(self, playerId):
        """
        Scale player cost such that best response is in interval [-30,0] dBm. Take into account measured cross gain.

        Keyword arguments:
        playerId -- numerical id identifying player.
        """
        omega = self.crossGainsTotalInterf[playerId] / float(self.directGains[playerId])
        newCost = 1 / (float(1e-3 + omega)) + self.players[playerId].getPlayerCost() * (
            1 / (float(1e-6 + omega)) - 1 / (float(1e-3 + omega)))
        self.players[playerId].setScaledCost(newCost)

    def isConvergent(self, restrictiveCond):
        """Check convergence rule"""
        if checkConvergenceRule(self.nrPlayers, self.directGains, self.crossGains, restrictiveCond):
            return True

        return False

    def areElemEqual(self, logicatDictionary, value=True):
        """
        Check if all elements of a dictionary, containing logical values, are equal

        logicalDicitonary -- python dictionary with logical values, True/False
        value -- logical value to compare with
        """
        for key in logicatDictionary:
            if logicatDictionary[key] != value:
                return False
        return True

    def resetLogicalDictionary(self, dictToReset, value=False):
        """
        Set all values of logical dictionary to False or True

        dictToReset -- python dictionary containing logical values True/False
        value -- set all elements of dictionary to this value
        """
        for key in dictToReset:
            dictToReset[key] = False

    def checkPowerEvent(self):
        """Check if someone has changed his transmitting power"""
        for key in self.playerEvent:
            if self.playerEvent[key]:
                return True
        return False

    def checkPowerEventWithMask(self, mask):
        """
        Check if someone has changed his transmitting power. Consider
        only the players for which mask is True.
        """
        for key in self.playerEvent:
            if self.playerEvent[key] and mask[key]:
                return True
        return False

    def isGameInEquilibrium(self):
        """Check to see if all players reached a stable state"""
        for key in self.equilibrium:
            if not self.equilibrium[key]:
                return False
        return True

    def generateRandomPowerEvent(self):
        """Choose a random player and change its transmitting power"""
        keyPl = random.choice(self.playerEvent.keys())
        # self.players[keyPl].generatePowerEvent()
        # self.playersCrtTxPovers[keyPl] = self.players[keyPl].physicalLayer.getCrtTxPower()
        self.playersCrtTxPovers[keyPl] = self.players[keyPl].generatePowerEvent()
        self.playerEvent[keyPl] = True

    def writeStatToFile(self, nrGameIterations, fileResults, multiRun=False, nrRun=0):
        """
        Write statistics to file.
        Write something like this:
        [run number - if multiple runs],game iterations, player number, player iteration, player cost, player scaled cost, player crt discrete tx power, player crt real tx power
        """
        for key in self.players:
            self.writePlayerStatToFile(key, nrGameIterations, fileResults, multiRun, nrRun)

    def writePlayerStatToFile(self, playerId, nrGameIterations, fileResults, multiRun=False, nrRun=0):
        """
        Write statistics to file for one player.
        Write something like this:
        [run number - if multiple runs],game iterations, player number, player iteration, player cost, player scaled cost, player crt discrete tx power, player crt real tx power
        """
        playerStats = [nrGameIterations, self.players[playerId].playerNumber, self.players[playerId].playerIterations,
                       self.players[playerId].cost, self.players[playerId].scaledCost,
                       self.players[playerId].physicalLayer.getCrtTxPower(),
                       self.players[playerId].physicalLayer.getBestRespUntouched()]
        if multiRun:
            playerStats.insert(0, nrRun)
        writeListToFile(fileResults, playerStats)

    def updatePlayersTxPowers(self, playerId, crtTxPower):
        """Just update the list with players current tx power"""
        self.playersCrtTxPovers[playerId] = crtTxPower

    def measureInterferenceForPlayerI(self, playerId):
        """
        Measure the received power level for player i.
        received power level = sum(h_ji * p_j) + n_0.

        Keyword arguments:
        playerId -- Numerical id for player that will measure power (sense spectrum)
        """
        transmissionTime = 7
        # configure transmitters
        for key in self.players:
            if key != playerId:
                self.players[key].physicalLayer.transmitObject.sendData(transmissionTime)
        # wait a second just to be sure that receiver senses the signal generated.
        time.sleep(0.5)
        # measure power
        receivedPower = self.players[playerId].physicalLayer.senseObject.quickSense()

        # wait for all transmissions to finish
        for key in self.players:
            if key != playerId:
                self.players[key].physicalLayer.transmitObject.sleepUntilSignalGenerationStops()

        return receivedPower

    def playGame(self, nrRuns):
        """
        Play the power allocation game, play the specified type of game

        Keyword arguments:
        number of independent games played.
        nrRuns -- number of independent games to be played.
        """
        if self.gameType == 1:
            filePathResults = getFilePathWithDate(self.coordId, 1)
            self.playGameType1(filePathResults)
        elif self.gameType == 2:
            filePathResults = getFilePathWithDate(self.coordId, 2)
            self.playGameType2(filePathResults)
        elif self.gameType == 3:
            filePathResults = getFilePathWithDate(self.coordId, 3, True)
            self.playGameType3(nrRuns, filePathResults)
        elif self.gameType == 11:
            filePathResults = getFilePathWithDate(self.coordId, 1, True)
            self.playGameType1MultiRun(nrRuns, filePathResults)
        elif self.gameType == 4:
            filePathResults = getFilePathWithDate(self.coordId, 4, True)
            self.playGameType4(nrRuns, filePathResults)
        elif self.gameType == 5:
            filePathResults = getFilePathWithDate(self.coordId, 5, False)
            self.playGameType5(filePathResults)
        elif self.gameType == 6:
            filePathResults = getFilePathWithDate(self.coordId, 6, False)
            self.play_game_type_6(filePathResults)
        else:
            print "Game type %d not implemented!!!" % (self.gameType)
            return

    def playGameType1(self, statFilePath, multipleRuns=False, run=0):
        """
        Game type 1:
        - gains are measured at the beginning and considered constant during the game
        - powerGame class keeps a record of each player transmitting power during each iteration
        - power game class keeps record of players that have reached equilibrium

        Keyword arguments:
        statFilePath -- path to file where statistics are saved.
        multipleRuns -- if True then save statistic in the same file, add run number in statistics file.
        run -- current independent run, consider this only for a multiple run game, i.e. multipleRuns = True
        """
        print "Game type 1 started"
        gameIterations = 0
        timeStartGame = time.time()
        oldPlayerPowerEvent = dict()

        # generate a power event, for a random player, in order to start the game
        self.generateRandomPowerEvent()
        # self.playerEvent[0] = True

        # just print initial state
        self.writeStatToFile(gameIterations, statFilePath, multipleRuns, run)

        # infinite loop
        while True:
            # stop the game if all players have reached a stable state, i.e. equilibrium
            if self.isGameInEquilibrium():
                print "Players have reached a stable state."
                break

            # check for power event
            if self.checkPowerEvent():
                gameIterations += 1
                print "Game iteration %d" % (gameIterations)
                # reset power event
                oldPlayerPowerEvent = copy.deepcopy(self.playerEvent)
                self.resetLogicalDictionary(self.playerEvent)

                if self.areElemEqual(oldPlayerPowerEvent, True):
                    # all players have changed strategy, all players must react and compute best response
                    self.resetLogicalDictionary(oldPlayerPowerEvent, False)

                for key in oldPlayerPowerEvent:
                    # compute best response for each player that did not changed their transmission power
                    if not oldPlayerPowerEvent[key]:
                        # check if the number of player iterations > some threshold (in this way avoid infinite
                        # loops, in some cases 3 strategies repeat with period 3-4 -> infinite loop)
                        if self.players[key].getNrPlayerIterations() > self.playerIterationsThreshold:
                            # compute strategy as average over last 5 strategies
                            self.players[key].updateTxPowerAsAverage()
                            self.equilibrium[key] = True
                        else:
                            self.scaleCostForPlayer(key)
                            self.playerEvent[key] = self.players[key].updateTxPower(self.playersCrtTxPovers,
                                                                                    self.crossGains[key])
                            # check if player has reached a stable state
                            self.equilibrium[key] = self.players[key].isInEquilibrium()
                        self.updatePlayersTxPowers(key, self.players[key].physicalLayer.getCrtTxPower())
                        # save statistics to file
                        self.writePlayerStatToFile(key, gameIterations, statFilePath, multipleRuns, run)
                    else:
                        # check if player has reached a stable state
                        self.equilibrium[key] = self.players[key].isInEquilibrium()
                        # if not oldPlayerPowerEvent[key]:
                        # self.scaleCostForPlayer(key)
                        # self.playerEvent[key] = self.players[key].updateTxPower(self.playersCrtTxPovers, self.crossGains[key])
                        # self.updatePlayersTxPowers(key, self.players[key].physicalLayer.getCrtTxPower())
                        # self.writePlayerStatToFile(key, gameIterations, statFilePath, multipleRuns, run)
                        # self.equilibrium[key] = self.players[key].isInEquilibrium()
            else:
                # check if all players reached a stable state, i.e. in equilibrium
                if not self.isGameInEquilibrium():
                    # game has not reached a stable state
                    for key in self.playerEvent: self.playerEvent[key] = True
                else:
                    # all players in equilibrium, stop the game
                    print "Players have reached a stable state."
                    break
        print "Game type 1 finished in %d steps (1 step = players react to another player event) and %.3f seconds." % (
            gameIterations, time.time() - timeStartGame)
        print "For two players 10 steps = each player makes 5 moves."

    def playGameType1MultiRun(self, nrRuns, statFilePath):
        """
        Game type 1, run the game multiple times:
        - gains are measured at the beginning and considered constant during the game
        - powerGame class keeps a record of each player transmitting power during each iteration
        - power game class keeps record of players that have reached equilibrium

        Keyword arguments:
        nrRuns -- number of independent games played.
        statFilePath -- path to file where statistics are saved.
        """
        for crtRun in range(nrRuns):
            print "Crt run: %d" % (crtRun)
            if crtRun > 0:
                self.resetGame()
            self.playGameType1(statFilePath, True, crtRun)
        print "Finish!! :)(:"

    def playGameType2(self, statFilePath):
        """
        Live version of the game. Gains are measured when each player changes its transmitting power.
        - gains are measured at the beginning of the game
        - cross gains are measured for each player when he wants to update power tx level
        - powerGame class keeps a record of each player transmitting power during each iteration
        - power game class keeps record of players that have reached equilibrium

        Keyword argumetns:
        statFilePath -- path to file where statistics are saved.
        """
        iterations = 0
        timeStartGame = time.time()
        print "Game type 1 started"

        oldPlayerPowerEvent = dict()

        # generate a power event, for a random player, in order to start the game
        self.generateRandomPowerEvent()

        # just print initial state
        self.writeStatToFile(iterations, statFilePath)

        # infinite loop
        while True:
            # stop the game if all players have reached a stable state, i.e. equilibrium
            if self.isGameInEquilibrium():
                print "Players have reached a stable state."
                break

            # check for an event
            if self.checkPowerEvent():
                iterations += 1
                print "Iteration %d" % (iterations)
                # reset power event
                oldPlayerPowerEvent = copy.deepcopy(self.playerEvent)
                self.resetLogicalDictionary(self.playerEvent)

                if self.areElemEqual(oldPlayerPowerEvent, True):
                    # all players have changed strategy, all players must react and compute best response
                    self.resetLogicalDictionary(oldPlayerPowerEvent, False)

                for key in oldPlayerPowerEvent:
                    # compute best response for each player that did not changed their transmission power
                    # these players will generate a power event
                    if not oldPlayerPowerEvent[key]:
                        # measure cross gains and noise for player
                        measuredPower = self.measureInterferenceForPlayerI(key)
                        self.playerEvent[key] = self.players[key].updateTxPower1(measuredPower)
                        # scale costs based on direct and cross gains
                        self.scaleCostForPlayer(key)

                    # check if player has reached a stable state
                    self.equilibrium[key] = self.players[key].isInEquilibrium()
                # save stats to file
                self.writeStatToFile(iterations, statFilePath)

            else:
                # check if all players reached a stable state, i.e. in equilibrium
                if not self.isGameInEquilibrium():
                    # game has not reached a stable state
                    for key in self.playerEvent:
                        self.playerEvent[key] = True
                else:
                    # all players in equilibrium, stop the game
                    print "Players have reached a stable state."
                    break
        print "Game type 1 finished in %d steps (1 step = players react to another player event) and %.3f seconds." % (
            iterations, time.time() - timeStartGame)
        print "For two players 10 steps = each player makes 5 moves."

    def playGameType3(self, nrRuns, statFile):
        """
        Game Type 1, run the game multiple times and do a sweep for costs
        - find the cost influence on the results: vary cost in [0,1], for each cost do the experiment nrRuns times
        - costs are adapted dynamically during the game
        - gains are measured at the beginning and considered constant during the game
        - powerGame class keeps a record of each player transmitting power during each iteration
        - power game class keeps record of players that have reached equilibrium
        - measure gains at the beginning of each run

        Keyword arguments:
        nrRuns -- number of independent games played.
        statFile -- path to file where statistics are saved.
        """
        step = 0.05
        crtCost = 0.0
        # TODO: need to check convergence rule after each gain measurement
        for crtCostRun in range(int(1 / step)):
            print "Current Cost run: %d." % (crtCostRun)
            # do specified nr of independent runs for given cost
            for key in self.players:
                # all players have the same cost
                self.players[key].setPlayerCost(crtCost)
            # must measure gains for first run and scale costs according to current situation
            self.useDummyData = True

            self.resetGame()
            # use this measured gains for the nrRuns runs
            # self.dummyDirectGains = copy.deepcopy(self.directGains)
            # self.dummyCrossGains = copy.deepcopy(self.crossGains)
            # self.useDummyData = True
            for crtRun in range(nrRuns):
                print "Current run: %d" % (crtRun)
                if crtRun > 0:
                    self.resetGame()
                # play the game
                self.playGameType1(statFile, True, crtRun)
            # increment costs
            crtCost += step
        print "Finish! :)(:"

    def playGameStep(self, statFilePath, multipleRuns=False, run=0):
        """
        Game step:
        - gains are measured at the beginning and considered constant during the game
        - powerGame class keeps a record of each player transmitting power during each iteration
        - power game class keeps record of players that have reached equilibrium

        Keyword arguments:
        statFilePath -- path to file where statistics are saved.
        multipleRuns -- if True then save statistic in the same file, add run number in statistics file.
        run -- current independent run, consider this only for a multiple run game, i.e. multipleRuns = True
        """
        gameIterations = 0
        timeStartGame = time.time()
        oldPlayerPowerEvent = dict()
        # generate a power event, for a random player, in order to start the game
        self.generateRandomPowerEvent()
        # just print initial state
        self.writeStatToFile(gameIterations, statFilePath, multipleRuns, run)
        # infinite loop
        measureP = False
        while True:
            # stop the game if all players have reached a stable state, i.e. equilibrium
            if self.isGameInEquilibrium():
                print "Players have reached a stable state."
                break
            # check for power event
            if self.checkPowerEvent():
                gameIterations += 1
                print "Game iteration %d" % (gameIterations)

                if measureP and self.useDummyData == False:
                    self.measureGainsV2()

                # reset power event
                oldPlayerPowerEvent = copy.deepcopy(self.playerEvent)
                self.resetLogicalDictionary(self.playerEvent)

                if self.areElemEqual(oldPlayerPowerEvent, True):
                    # all players have changed strategy, all players must react and compute best response
                    self.resetLogicalDictionary(oldPlayerPowerEvent, False)

                for key in oldPlayerPowerEvent:
                    # compute best response for each player that did not changed their transmission power
                    if not oldPlayerPowerEvent[key]:
                        # check if the number of player iterations > some threshold (in this way avoid infinite
                        # loops, in some cases 3 strategies repeat with period 3-4 -> infinite loop)
                        if self.players[key].getNrPlayerIterations() > self.playerIterationsThreshold:
                            # compute strategy as average over last 5 strategies
                            self.players[key].updateTxPowerAsAverage()
                            self.equilibrium[key] = True
                        else:
                            # daca se depasesc 5 iteratii -> mai fac o masuratoare
                            if self.players[key].getNrPlayerIterations() % 5 == 0 and self.players[
                                key].getNrPlayerIterations() > 4:
                                measureP = True

                            self.scaleCostForPlayerV2(key)
                            self.playerEvent[key] = self.players[key].updateTxPower2(self.crossGainsTotalInterf[key])
                            # check if player has reached a stable state
                            self.equilibrium[key] = self.players[key].isInEquilibrium()
                        self.updatePlayersTxPowers(key, self.players[key].physicalLayer.getCrtTxPower())
                        # save statistics to file
                        self.writePlayerStatToFile(key, gameIterations, statFilePath, multipleRuns, run)
                    else:
                        # check if player has reached a stable state
                        self.equilibrium[key] = self.players[key].isInEquilibrium()
            else:
                # check if all players reached a stable state, i.e. in equilibrium
                if not self.isGameInEquilibrium():
                    # game has not reached a stable state
                    for key in self.playerEvent: self.playerEvent[key] = True
                else:
                    # all players in equilibrium, stop the game
                    print "Players have reached a stable state."
                    break
        print "Game type 1 finished in %d steps (1 step = players react to another player event) and %.3f seconds." % (
            gameIterations, time.time() - timeStartGame)
        print "For two players 10 steps = each player makes 5 moves."


    def playGameType4(self, nrRuns, statFile):
        """
        Game Type 1, run the game multiple times and do a sweep for costs
        - find the cost influence on the results: vary cost in [0,1], for each cost do the experiment nrRuns times
        - costs are adapted dynamically during the game
        - gains are measured at the beginning and each 5 player iterations
        - powerGame class keeps a record of each player transmitting power during each iteration
        - power game class keeps record of players that have reached equilibrium
        - measure gains at the beginning of each run

        Keyword arguments:
        nrRuns -- number of independent games played.
        statFile -- path to file where statistics are saved.
        """
        self.measureGainsV2()
        if checkConvergenceRuleTotalInterf(self.nrPlayers, self.directGains, self.crossGainsTotalInterf, False):
            print "Game is convergent"
            print "h_ii ", self.directGains
            print "h_ji ", self.crossGainsTotalInterf
        else:
            print "Topology not convergent"

        step = 0.05
        crtCost = 0.0
        for crtCostRun in range(int(1 / step)):
            print "Current Cost run: %d." % (crtCostRun)
            for key in self.players:
                self.players[key].setPlayerCost(crtCost)
            if crtCostRun > 0:
                self.useDummyData = True
                self.resetGame()
                # if crtCostRun > 0:
                # first set of measurements form convergence condition
            # self.useDummyData=False
            # self.resetGame()
            self.dummyDirectGains = copy.deepcopy(self.directGains)
            self.dummyCrossGainsTotalInterf = copy.deepcopy(self.crossGainsTotalInterf)
            self.useDummyData = True
            for crtRun in range(nrRuns):
                print "Current run: %d" % (crtRun)
                if crtRun > 0:
                    self.resetGame()
                # play the game
                self.playGameStep(statFile, True, crtRun)
            crtCost += step
        print "Finish! :)(:"

    def playGameType5(self, statFilePath):
        """Dynamic game, i.e. players come and go. All 4 players must play initially."""
        print "Game type 5 started."

        # self.measureGains()

        gameIterations = 0
        oldPlayerPowerEvent = dict()
        # used to determine which player plays the power allocation game
        mask = dict()
        chei = list()
        step_tx_powers = list()
        for key in self.players:
            self.players[key].setPlayerCost(self.playerCostList[key])
            mask[key] = True
            chei.append(key)
            step_tx_powers.append(0)
        self.generateRandomPowerEvent()
        self.writeStatToFile(gameIterations, statFilePath)

        if self.plot_results:
            for key in chei:
                step_tx_powers[key] = self.players[key].physicalLayer.getCrtTxPower()
            self.my_plot.plot_tx_powers(step_tx_powers)

        while True:
            print "game iteration %d" % (gameIterations + 1)
            # if gameIterations == 12:
            # print "stop"
            if gameIterations == 50:
                break
            elif gameIterations == 13:
                mask[3] = False
                print "Player 4 has left the game!!"
                self.resetLogicalDictionary(self.playerEvent, False)
                self.playerEvent[0] = True
            elif gameIterations == 26:
                mask[3] = True
                print "Player 4 rejoins the game!!"
                self.resetLogicalDictionary(self.playerEvent, False)
                self.playerEvent[3] = True
            elif gameIterations == 39:
                mask[3] = False
                mask[2] = False
                print "players 3 and 4 leave the game!!"
                self.resetLogicalDictionary(self.playerEvent, False)
                self.playerEvent[1] = True
            if self.checkPowerEventWithMask(mask):
                gameIterations += 1
                oldPlayerPowerEvent = copy.deepcopy(self.playerEvent)
                self.resetLogicalDictionary(self.playerEvent, False)

                for key in oldPlayerPowerEvent:
                    if not oldPlayerPowerEvent[key] and mask[key]:
                        if self.players[key].getNrPlayerIterations() > self.playerIterationsThreshold:
                            self.players[key].updateTxPowerAsAverage()
                            self.equilibrium[key] = True
                        else:
                            self.scaleCostForPlayerWithMask(key, mask)
                            self.playerEvent[key] = self.players[key].updateTxPowerWithMask(self.playersCrtTxPovers,
                                                                                            self.crossGains[key], mask)
                            # check if player has reached a stable state
                            self.equilibrium[key] = self.players[key].isInEquilibrium()
                        self.updatePlayersTxPowers(key, self.players[key].physicalLayer.getCrtTxPower())
                    else:
                        # check if player has reached a stable state
                        self.equilibrium[key] = self.players[key].isInEquilibrium()

                # if required create plot
                if self.plot_results:
                    for i in chei:
                        if mask[i]:
                            step_tx_powers[i] = self.players[i].physicalLayer.getCrtTxPower()
                        else:
                            step_tx_powers[i] = -90
                    self.my_plot.plot_tx_powers(step_tx_powers)

            self.writeStatToFile(gameIterations, statFilePath)

    def play_game_type_6(self, stat_file_path):
        """
        Play the power allocation game for n game iterations. Channel measurement is performed at each at given intervals.
        :param stat_file_path:
        :return:
        """
        print "Game type 6 started"
        gameIterations = 0
        oldPlayerPowerEvent = dict()
        # used to determine which player plays the power allocation game
        chei = list()
        step_tx_powers = list()
        for key in self.players:
            self.players[key].setPlayerCost(self.playerCostList[key])
            chei.append(key)
            step_tx_powers.append(0)
        self.generateRandomPowerEvent()
        self.writeStatToFile(gameIterations, stat_file_path)

        if self.plot_results:
            for key in chei:
                step_tx_powers[key] = self.players[key].physicalLayer.getCrtTxPower()
            self.my_plot.plot_tx_powers(step_tx_powers)

        while True:
            print "game iteration %d" % (gameIterations + 1)
            if gameIterations >= self.nr_game_iterations:
                break
            elif gameIterations%self.measuring_period == 0 and gameIterations > 5:
                # perform channel mesurements
                self.measureGains()
            if self.checkPowerEvent():
                gameIterations += 1
                oldPlayerPowerEvent = copy.deepcopy(self.playerEvent)
                self.resetLogicalDictionary(self.playerEvent, False)

                for key in oldPlayerPowerEvent:
                    if not oldPlayerPowerEvent[key]:
                        if self.players[key].getNrPlayerIterations() > self.playerIterationsThreshold:
                            self.players[key].updateTxPowerAsAverage()
                            self.equilibrium[key] = True
                        else:
                            self.scaleCostForPlayer(key)
                            self.playerEvent[key] = self.players[key].updateTxPower(self.playersCrtTxPovers,
                                                                                    self.crossGains[key])
                            # check if player has reached a stable state
                            self.equilibrium[key] = self.players[key].isInEquilibrium()
                        self.updatePlayersTxPowers(key, self.players[key].physicalLayer.getCrtTxPower())
                    else:
                        # check if player has reached a stable state
                        self.equilibrium[key] = self.players[key].isInEquilibrium()

                # if required create plot
                if self.plot_results:
                    for i in chei:
                        step_tx_powers[i] = self.players[i].physicalLayer.getCrtTxPower()
                    self.my_plot.plot_tx_powers(step_tx_powers)

            self.writeStatToFile(gameIterations, stat_file_path)