Example #1
0
def disj_parity_win(g, maxValues, k, u):
    """
    Recursive solver for generalized parity games. Implements the classical algorithm which solves generalized parity
    games.
    :param g: the game to solve
    :param maxValues: the maximum value according to each priority function
    :param k: the number of priority functions
    :param u: integer for testing purposes
    :return: W1, W2 the winning regions in the game for player 1 and player 2 (for the original game, without complement)
    """

    # Base case : all maxValues are 1 or the game is empty
    if all(value == 1 for value in maxValues) or len(g.nodes) == 0:
        return g.get_nodes(), []

    for i in range(k):
        attMaxOdd, compl_attMaxOdd = reachability.attractor(
            g, ops.i_priority_node_function_j(g, maxValues[i], i + 1), 0)
        G1 = g.subgame(compl_attMaxOdd)
        attMaxEven, compl_attMaxEven = reachability.attractor(
            G1, ops.i_priority_node_function_j(G1, maxValues[i] - 1, i + 1), 1)
        H1 = G1.subgame(compl_attMaxEven)
        j = 0
        while True:
            j += 1
            copy_maxValues = copy.copy(maxValues)
            copy_maxValues[i] -= 2
            W1, W2 = disj_parity_win(H1, copy_maxValues, k, u + 1)

            if len(G1.nodes) == 0:
                break

            if set(W2) == set(H1.get_nodes()):
                B, compl_B = reachability.attractor(g, G1.get_nodes(), 1)
                W1, W2 = disj_parity_win(g.subgame(compl_B), maxValues, k,
                                         u + 1)
                B.extend(W2)
                return W1, B

            T, compl_T = reachability.attractor(G1, W1, 0)
            G1 = G1.subgame(compl_T)
            E, compl_E = reachability.attractor(
                G1, ops.i_priority_node_function_j(g, maxValues[i] - 1, i + 1),
                0)
            H1 = G1.subgame(compl_E)
    return g.get_nodes(), []
Example #2
0
def reduction_to_safety_parity_solver(graph):
    """
    Main function which solves a parity game by reduction to a safety game
    :param graph: the arena of the parity game
    :return: the winning regions in the parity game
    """
    # First we find out the number of counters necessary
    maximum = -1
    for node in graph.get_nodes():
        if (graph.get_node_priority(node) > maximum):
            maximum = graph.get_node_priority(node)
    if maximum % 2 == 0:
        maxOdd = maximum - 1
    else:
        maxOdd = maximum

    nbr_counters = (maxOdd // 2) + 1
    max_counter = [0] * nbr_counters

    # counts every odd priority
    for node in graph.get_nodes():
        if (graph.get_node_priority(node) % 2 != 0):
            position = graph.get_node_priority(node) // 2
            max_counter[position] = max_counter[position] + 1

    # Then we build the start counter for each node, we only build the part reachable from (v, 0, ..., 0) for all v
    # For convenience, we label the nodes using the str representation of their list  : [v, c_1, c_2, ..., c_k]
    # This is a unique identifier for each node.
    # The overflow is simply written "-"

    # Start nodes for construction : (v, 0, ..., 0)
    start_nodes = []
    for node in graph.get_nodes():
        start_nodes.append([node] + [0] * nbr_counters)
    transformed_graph = createSafetyGame(
        graph, start_nodes,
        max_counter)  # creates the safety game from the start nodes

    W1bis, W2bis = reachability.attractor(
        transformed_graph, ["-"],
        1)  # attractor for player 2 of the nodes in overflow
    W1 = []
    W2 = []

    empty_counters = [0] * nbr_counters  # [0, ..., 0]
    # checks if the nodes [v, 0, ..., 0] belongs to the attractor or not and creates the winning regions
    for node in W1bis:
        if node != "-":
            node_list = ast.literal_eval(node)
            if node_list[-nbr_counters:] == empty_counters:
                W2.append(node_list[0])
    for node in W2bis:
        if node != "-":
            node_list = ast.literal_eval(node)
            if node_list[-nbr_counters:] == empty_counters:
                W1.append(node_list[0])
    return W1, W2
Example #3
0
def disj_parity_win2(g, maxValues, k, u):
    """
    Recursive solver for generalized parity games. Uses the algorithm presented in
    http://www2.eecs.berkeley.edu/Pubs/TechRpts/2006/EECS-2006-144.html
    This is used for testing purposes.
    :param g: the game to solve
    :param maxValues: the maximum value for each priority function
    :param k: the number of priority functions
    :param u: integer for testing purposes
    :return: W1, W2 the winning regions in the game for player 1 and player 2 (for the base game)
    """

    # Base case : all maxValues are 1 or the game is empty
    if all(value == 1 for value in maxValues) or len(g.nodes) == 0:
        #print(str(u*2*" ")+"it-"+str(u)+" return on base case")
        return g.get_nodes(), []

    for i in range(k):
        max = ops.max_priority(g)
        if max % 2 == 0:
            even = max
            attMaxOdd, compl_attMaxOdd = [], []
            #print(str(u*2*" ")+"it-"+str(u)+" maxOdd-"+str(maxValues[i])+" attMaxOdd-"+str(attMaxOdd)+" "+"complAttMaxOdd-"+str(compl_attMaxOdd))
            G1 = g
            attMaxEven, compl_attMaxEven = reachability.attractor(
                G1, ops.i_priority_node_function_j(G1, max, i + 1), 1)
            #print(str(u*2*" ")+"it-"+str(u)+" maxEven-"+str(maxValues[i]-1)+" attMaxEven-"+str(attMaxEven)+" "+"complAttMaxEven-"+str(compl_attMaxEven))
            H1 = G1.subgame(compl_attMaxEven)
            j = 0
            #print(str(u*2*" ")+"it-"+str(u)+" G\n"+str(G1))
            #print(str(u*2*" ")+"it-"+str(u)+" H\n"+str(H1))
        else:
            even = max - 1
            attMaxOdd, compl_attMaxOdd = reachability.attractor(
                g, ops.i_priority_node_function_j(g, max, i + 1), 0)
            # print(str(u*2*" ")+"it-"+str(u)+" maxOdd-"+str(maxValues[i])+" attMaxOdd-"+str(attMaxOdd)+" "+"complAttMaxOdd-"+str(compl_attMaxOdd))
            G1 = g.subgame(compl_attMaxOdd)
            attMaxEven, compl_attMaxEven = reachability.attractor(
                G1, ops.i_priority_node_function_j(G1, max - 1, i + 1), 1)
            # print(str(u*2*" ")+"it-"+str(u)+" maxEven-"+str(maxValues[i]-1)+" attMaxEven-"+str(attMaxEven)+" "+"complAttMaxEven-"+str(compl_attMaxEven))
            H1 = G1.subgame(compl_attMaxEven)
            j = 0
            # print(str(u*2*" ")+"it-"+str(u)+" G\n"+str(G1))
            # print(str(u*2*" ")+"it-"+str(u)+" H\n"+str(H1))
        while True:

            j += 1
            copy_maxValues = copy.copy(maxValues)
            copy_maxValues[i] -= even - 1
            W1, W2 = disj_parity_win2(H1, copy_maxValues, k, u + 1)
            #print(str(u * 2 * " ") + "it-" + str(u)+"-"+str(j) + " W1-" + str(W1) + " W2-" + str(W2))

            #print("W1 "+str(W1))
            #print("W2 "+str(W2))
            #print("game " + str(g) + "att " + str(attMaxOdd) + "compl " + str(compl_attMaxOdd))
            #print("stop "+str(set(W2))+" "+str(set(H1.get_nodes()))+" val "+ str(set(W2) == set(H1.get_nodes())))
            #break

            # cette cond etait en dessous de lautre et lautre prennait precedence quand on avait les 2
            #print(len(G1.nodes))
            if len(G1.nodes) == 0:
                #print("G empty")
                break

            if set(W2) == set(H1.get_nodes()):
                #print("hello")
                B, compl_B = reachability.attractor(g, G1.get_nodes(), 1)
                W1, W2 = disj_parity_win2(g.subgame(compl_B), maxValues, k,
                                          u + 1)
                #print("re "+str(B)+" "+str(W1)+" "+str(W2))
                B.extend(W2)
                return W1, B
            #break
            T, compl_T = reachability.attractor(G1, W1, 0)
            G1 = G1.subgame(compl_T)
            E, compl_E = reachability.attractor(
                G1, ops.i_priority_node_function_j(g, even, i + 1), 0)
            H1 = G1.subgame(compl_E)
            #break
        #break
    return g.get_nodes(), []
Example #4
0
def strong_parity_solver_no_strategies(g):
    """
    Strong parity games solver. This is an implementation of the recursive algorithm used to solve parity games.
    This implementation does not compute the winning strategies (for comparison purpose with other algorithms
    which don't)
    :param g: the game to solve.
    :return: the solution in the following format : (W_0, sigma_0), (W_1, sigma_1).
    """
    W1 = []  # Winning region of player 0
    W2 = []  # Winning region of player 1

    # if the game is empty, return the empty regions
    if len(g.nodes) == 0:
        return W1, W2

    else:
        i = ops.max_priority(g)  # get max priority occurring in g

        # determining which player we are considering, if i is even : player 0 and else player 1
        if i % 2 == 0:
            j = 0
        else:
            j = 1

        opponent = ops.opponent(j)  # getting the opponent of the player

        U = ops.i_priority_node(g, i)  # target set for the attractor : nodes of priority i

        # getting the attractor A and discarding the region for the opponent
        A, discard1 = reachability.attractor(g,U,j)

        # The subgame G\A is composed of the nodes not in the attractor, thus the nodes of the opposite player's region
        G_A = g.subgame(discard1)

        # Recursively solving the subgame G\A, solution comes as (W_0, W_1)
        sol_player1, sol_player2 = strong_parity_solver_no_strategies(G_A)

        # depending on which player we are considering, assign regions to the proper variables
        # W'_j is noted W_j, sigma'_j is noted sig_j; the same aplies for jbar
        if j == 0:
            W_j = sol_player1
            W_jbar = sol_player2
        else:
            W_j = sol_player2
            W_jbar = sol_player1

        # if W'_jbar is empty we update the regions depending on the current player
        # the region for the whole game for one of the players is empty
        if not W_jbar:
            if j == 0:
                W1.extend(A)
                W1.extend(W_j)
            else:
                W2.extend(A)
                W2.extend(W_j)
        else:
            # compute attractor B
            B, discard1 = reachability.attractor(g, W_jbar, opponent)
            # The subgame G\B is composed of the nodes not in the attractor, so of the opposite player's winning region
            G_B = g.subgame(discard1)

            # recursively solve subgame G\B, solution comes as (W_0, W_1)
            sol_player1_, sol_player2_ = strong_parity_solver_no_strategies(G_B)

            # depending on which player we are considering, assign regions to the proper variables
            # W''_j is noted W__j, sigma''_j is noted sig__j; the same aplies for jbar
            if j == 0:
                W__j = sol_player1_
                W__jbar = sol_player2_
            else:
                W__j = sol_player2_
                W__jbar = sol_player1_

            # the last step is to update the winning regions depending on which player we consider
            if j == 0:
                W1 = W__j

                W2.extend(W__jbar)
                W2.extend(B)

            else:
                W2 = W__j

                W1.extend(W__jbar)
                W1.extend(B)

    return W1, W2