def structure_center_type(cgn): """Check whether a dicotic game is in the center. Parameters ---------- cgn : str Dicotic game in combinatorial game notation. Returns ------- center_type : {"not_center", "own_inverse", "inv_incomp"} The center type determines what kind of game in the center it is: * "not_center" : This game is not in the center * "own_inverse" : The game is equal to its own inverse and is therefore in the center. * "inv_incomp" : The game is incomparable with its own inverse and therefore in the center. """ # Create the game and compute its inverse g = Game(cgn) h = g.inverse() # Check whether this game is it's own inverse if str(g) == str(h): return "own_inverse" # Check whether the game is incomparable with its inverse if g.incomparable(h): return "inv_incomp" # This game is not in the center return "not_center"
def incomparable_pairs(games): """Compute incomparable pairs in a list of games. Parameters ---------- games : list List of games. Each game is represented as str in CGN. Returns ------- incomparable_pairs : dict Dictionary of sets of str of games. Each dictionary item is a set of games that are incomparable with the game that is the corresponding key. """ incomparable_pairs = dict() # Create keys with empty sets for g in games: incomparable_pairs[g] = set() # Check combinations for g, h in combinations(games, r=2): if Game(g).incomparable(Game(h)): incomparable_pairs[g].add(h) incomparable_pairs[h].add(g) # Return the dictionary return incomparable_pairs
def filter_unique_canonicals(str_canons): str_uniques = [] for g in str_canons: already = False for h in str_uniques: # Check whether G >= H was checked before ggeqh = str_geq_pairs.get((g, h)) if ggeqh is None: # It was not ggeqh = Game(g).geq(Game(h)) str_geq_pairs[(g, h)] = ggeqh # If G >= H, it might be that G == H if ggeqh: # Check whether H >= G was checked before hgeqg = str_geq_pairs.get((h, g)) if hgeqg is None: # It was not hgeqg = Game(h).geq(Game(g)) str_geq_pairs[(h, g)] = hgeqg # Check whether G == H if hgeqg: # Already in list, skip already = True break # Was G not already in the list? if not already: str_uniques.append(g) print("Canonical form {:d}:".format(len(str_uniques)), g) # Return the set of unique canonical forms return str_uniques
def test_str(self): g = Game() self.assertEqual(str(g), "0") g.left_options.append(Game()) self.assertEqual(str(g), "1") g.right_options.append(Game()) self.assertEqual(str(g), "*") g.left_options.clear() self.assertEqual(str(g), "-1")
def test_set_node_cgn(self): g = Game() num = g._set_node_cgn("{|}", 1) self.assertEqual(str(g), "0") self.assertEqual(num, 2) num = g._set_node_cgn("{|{|}}", 3) self.assertEqual(str(g), "0") self.assertEqual(num, 4) num = g._set_node_cgn("{{|}|{|}}", 1) self.assertEqual(str(g), "*") self.assertEqual(num, 8)
def generate_games_from_subsets(subsets: list): yield "0" for left, right in product(subsets, repeat=2): g = Game() # Add all options from the sets to the game if left: g.left_options.extend(Game(opt) for opt in left) if right: g.right_options.extend(Game(opt) for opt in right) # Yield the canonical form as str yield str(g.canonical_form())
def canonical_from_pair(pair: tuple): # Split pair in left and right options left, right = pair g = Game() # Add all options from the sets to the game if left: g.left_options.extend(Game(opt) for opt in left) if right: g.right_options.extend(Game(opt) for opt in right) # Return the canonical form as str return str(g.canonical_form())
def computeCombinations(sets): yield "0" # Create the cartesian product to create all combinations # of Left and Right option sets. for leftSet, rightSet in product(sets, repeat=2): g = Game() # Add all options from the sets to the game if leftSet: g.leftOptions.extend(Game(opt) for opt in leftSet) if rightSet: g.rightOptions.extend(Game(opt) for opt in rightSet) # Yield the canonical form yield str(g.canonicalForm())
def compute_combinations(sets): yield Game("0") # Create the cartesian product to create all combinations # of Left and Right option sets. for left_set, right_set in product(sets, repeat=2): g = Game() # Add all options from the sets to the game if left_set: g.left_options.extend(left_set) if right_set: g.right_options.extend(right_set) # Yield the canonical form yield g.canonical_form()
def create_network(canons): # Create network net = nx.DiGraph() # Check all (G, H) pairs num_perms = int(len(canons) * (len(canons) - 1)) perms = permutations(canons, 2) for G, H in tqdm(perms, total=num_perms): # Check whether G > H if Game(G).gtr(Game(H)): net.add_edge(G, H) # Return the transitive reduction red = nx.algorithms.dag.transitive_reduction(net) return red.edges
def test_get_node_cgn_dot(self): dot_str, num = Game()._get_node_cgn_dot("", 0) target_str = '\t0[label="0"];\n' self.assertEqual(dot_str, target_str) self.assertEqual(num, 0) dot_str, num = Game("1")._get_node_cgn_dot("abc", 2) target_str = 'abc\t2[label="1"];\n\t3[label="0"];\n\t2 -> 3[label="L"];\n' self.assertEqual(dot_str, target_str) self.assertEqual(num, 3) dot_str, num = Game("*2")._get_node_cgn_dot("xyz", -2) target_str = 'xyz\t-2[label="*2"];\n\t-1[label="0"];\n\t-2 -> -1[label="L"];\n\t0[label="*"];\n\t1[label="0"];\n\t0 -> 1[label="L"];\n\t2[label="0"];\n\t0 -> 2[label="R"];\n\t-2 -> 0[label="L"];\n\t3[label="0"];\n\t-2 -> 3[label="R"];\n\t4[label="*"];\n\t5[label="0"];\n\t4 -> 5[label="L"];\n\t6[label="0"];\n\t4 -> 6[label="R"];\n\t-2 -> 4[label="R"];\n' self.assertEqual(dot_str, target_str) self.assertEqual(num, 6)
def G0(depth: int = 1): subsets = [Game("0")] for i in range(depth): print("Depth is now {:d}.".format(i + 1)) # Find all subsets print("Creating the subset generator.") subsets = getAllSubsets(subsets) # print('There are {:d} subsets:'.format(len(subsets)), *subsets) # Remove sets with dominated options print("Filtering the non-dominated subsets.") nondomsets = filterDominatedSubsetsPar(subsets) print("There are {:d} subsets without any dominated options.".format(len(nondomsets))) # Compute the number of games with Left and Right a subset print("The number of possible games is {size:d}^2+1={tot:d}.".format(size=len(nondomsets), tot=len(nondomsets) ** 2 + 1)) # Find all of their canonical forms print("Creating the canonical form generator.") canons = compute_combinations(nondomsets) # Print the canonical forms and add them to a list for the next iteration # TODO: Parallelize? print("Filtering unique canonical forms.") subsets = [] for g in canons: if g not in subsets: # Only keep unique canonical forms subsets.append(g) print("Canonical form {:d}:".format(len(subsets)), g)
def test_repr(self): g = Game() self.assertEqual(g.__repr__(), "0") g.left_options.append(Game()) self.assertEqual(g.__repr__(), "1") g.right_options.append(Game()) self.assertEqual(g.__repr__(), "*") g.left_options.clear() self.assertEqual(g.__repr__(), "-1")
def test_get_cgn(self): g = Game() self.assertEqual(g.get_cgn(), "0") g.left_options.append(Game()) self.assertEqual(g.get_cgn(), "1") g.right_options.append(Game()) self.assertEqual(g.get_cgn(), "*") g.left_options.clear() self.assertEqual(g.get_cgn(), "-1")
def test_integer_value(self): # Check integer games self.assertEqual(Game("-3").integer_value(), -3) self.assertEqual(Game("-1").integer_value(), -1) self.assertEqual(Game("-2").integer_value(), -2) self.assertEqual(Game("0").integer_value(), 0) self.assertEqual(Game("1").integer_value(), 1) self.assertEqual(Game("2").integer_value(), 2) self.assertEqual(Game("3").integer_value(), 3) # Check ValueError on non-integer games with self.assertRaises(ValueError): Game("*").integer_value() with self.assertRaises(ValueError): Game("^").integer_value() with self.assertRaises(ValueError): Game("v").integer_value()
def checkSubset(subset_di): subset, di = subset_di for option_pair in permutations(subset, 2): # Check whether this pair was seen before outcome = di.get(option_pair) if outcome is None: # These games were not compared before outcome = Game(option_pair[0]).geq(Game(option_pair[1])) # Add to dict di[option_pair] = outcome if outcome: # option_pair[0] >= option_pair[1], subset contains dominated options return None # No dominated options, return this set return subset
def to_canonical_cgn(game): """Convert a game to its canoncial form in CGN. Parameters ---------- game : str The game to convert in combinatorial game notation. """ canonical = str(Game(game).canonical_form()) return f"{datetime.now():%Y-%m-%d %H:%M:%S};{mp.current_process().name};{game};{canonical}\n"
def test_get_cgn_dot(self): comp_str = Game().get_cgn_dot() target_str = 'digraph Game {\n\t0[label="0"];\n}' self.assertEqual(comp_str, target_str) comp_str = Game("1").get_cgn_dot() target_str = 'digraph Game {\n\t0[label="1"];\n\t1[label="0"];\n\t0 -> 1[label="L"];\n}' self.assertEqual(comp_str, target_str) comp_str = Game("*").get_cgn_dot() target_str = 'digraph Game {\n\t0[label="*"];\n\t1[label="0"];\n\t0 -> 1[label="L"];\n\t2[label="0"];\n\t0 -> 2[label="R"];\n}' self.assertEqual(comp_str, target_str) comp_str = Game("-1").get_cgn_dot() target_str = 'digraph Game {\n\t0[label="-1"];\n\t1[label="0"];\n\t0 -> 1[label="R"];\n}' self.assertEqual(comp_str, target_str) comp_str = Game("*2").get_cgn_dot() target_str = 'digraph Game {\n\t0[label="*2"];\n\t1[label="0"];\n\t0 -> 1[label="L"];\n\t2[label="*"];\n\t3[label="0"];\n\t2 -> 3[label="L"];\n\t4[label="0"];\n\t2 -> 4[label="R"];\n\t0 -> 2[label="L"];\n\t5[label="0"];\n\t0 -> 5[label="R"];\n\t6[label="*"];\n\t7[label="0"];\n\t6 -> 7[label="L"];\n\t8[label="0"];\n\t6 -> 8[label="R"];\n\t0 -> 6[label="R"];\n}' self.assertEqual(comp_str, target_str)
def test_clear(self): g = Game() g.left_options.append(Game()) g.right_options.append(Game()) g.clear() self.assertFalse(g.left_options) self.assertFalse(g.right_options)
def test_incomparable_zero(self): self.assertTrue(Game("*").incomparable_zero()) self.assertFalse(Game("1").incomparable_zero()) self.assertFalse(Game("^").incomparable_zero()) self.assertFalse(Game("0").incomparable_zero()) self.assertFalse(Game("v").incomparable_zero()) self.assertFalse(Game("-1").incomparable_zero())
def test_equal_zero(self): self.assertTrue(Game("0").equal_zero()) self.assertFalse(Game("1").equal_zero()) self.assertFalse(Game("^").equal_zero()) self.assertFalse(Game("*").equal_zero()) self.assertFalse(Game("v").equal_zero()) self.assertFalse(Game("-1").equal_zero())
def to_canonical(games): """Convert games to their canoncial forms. Parameters ---------- games : iterable The games to convert, each in combinatorial game notation. Yields ------ canonical : str A game in canonical form. Notes ----- The order of the converted games is preserverd. """ for g in games: yield str(Game(g).canonical_form())
def test_incomparable(self): self.assertTrue(Game("0").incomparable(Game("*"))) self.assertTrue(Game("1").incomparable(Game("1") + Game("*"))) self.assertTrue((Game("1") - Game("1")).incomparable(Game("*"))) self.assertTrue(Game("*").incomparable(Game("0").inverse()))
def test_equal(self): self.assertTrue(Game("0").equal(Game("0"))) self.assertTrue(Game("1").equal(Game("1"))) self.assertTrue((Game("1") - Game("1")).equal(Game("0"))) self.assertTrue(Game("*").equal(Game("*").inverse()))
def test_outcome_class(self): # First player win self.assertEqual(Game("*").outcome_class(), "N") self.assertEqual(Game("*2").outcome_class(), "N") self.assertEqual(Game("{^|v}").outcome_class(), "N") # Second player win self.assertEqual(Game().outcome_class(), "P") self.assertEqual(Game("0").outcome_class(), "P") self.assertEqual(Game("{*|*}").outcome_class(), "P") # Win for Left self.assertEqual(Game("1").outcome_class(), "L") self.assertEqual(Game("^").outcome_class(), "L") self.assertEqual(Game("{1|^}").outcome_class(), "L") # Win for Right self.assertEqual(Game("-1").outcome_class(), "R") self.assertEqual(Game("v").outcome_class(), "R") self.assertEqual(Game("{-1|v}").outcome_class(), "R")
def test_set_cgn(self): g = Game().set_cgn("0") self.assertEqual(str(g), "0") with self.assertRaises(ValueError): Game().set_cgn("abc")
def test_companion(self): # First player win self.assertEqual(str(Game("*").companion()), "{*|*}") self.assertEqual(str(Game("*2").companion()), "{*,{*|*}|*,{*|*}}") self.assertEqual(str(Game("{^|v}").companion()), "{{*,0|{*|*}}|{{*|*}|*,0}}") # Second player win self.assertEqual(str(Game().companion()), "*") self.assertEqual(str(Game("0").companion()), "*") self.assertEqual(str(Game("{*|*}").companion()), "*") # Win for Left self.assertEqual(str(Game("1").companion()), "{*,0|}") self.assertEqual(str(Game("^").companion()), "{*,0|{*|*}}") self.assertEqual(str(Game("{1|^}").companion()), "{0,{*,0|}|{*,0|{*|*}}}") # Win for Right self.assertEqual(str(Game("-1").companion()), "{|*,0}") self.assertEqual(str(Game("v").companion()), "{{*|*}|*,0}") self.assertEqual(str(Game("{-1|v}").companion()), "{{|*,0}|0,{{*|*}|*,0}}")
def test_canonical_form(self): self.assertTrue((Game("*") + Game("*")).canonical_form().equal_zero()) self.assertEqual(str(Game("{^,*|^,0}").canonical_form()), "^*") self.assertEqual(str(Game("^*").canonical_form()), "^*") self.assertEqual(str(Game("{2,1,0|-1,-3,2}").canonical_form()), "{{1|}|{|{|-1}}}") self.assertEqual(str(Game("*").canonical_form()), "*") self.assertEqual( str(Game("{{*,0|{*|*}},{*|*}|*,{*|*,{*|*}}}").canonical_form()), "{0,^*|*,v}") self.assertEqual( str( Game( "{*2,*3,{0|v*},{^|0,v*}|*,*2,*3,{*,^|0,v*},{0,^*|*,v},{0,^*|0,v*},{^,^*|v,v*}}" ).canonical_form()), "{0|*,*2,*3,{0,^*|*,v},{0,^*|0,v*}}") self.assertEqual( str( Game("{*,0,{*,*2,0|*,0},{*,^|v,v*}|*,*2,*3,{*,^|*,v}}"). canonical_form()), "{0|*,*2,*3}") self.assertEqual( str( Game( "{{*,^|*,0},{0,^*|*,0},{^,^*|0,v*}|*,{0,^*|*,v},{^*|0},{^,^*|v,v*}}" ).canonical_form()), "{{*,^|*,0},{0,^*|*,0},{^,^*|0,v*}|0}") self.assertEqual( str( Game( "{{*,*2|0},{*,0|*,*2,0},{^,^*|v,v*}|v,{*,0|*,v},{*,^|v,v*},{^*|v},{^|*,0}}" ).canonical_form()), "{{*,*2|0},{*,0|*,*2,0},{^,^*|v,v*}|0}") self.assertEqual( str( Game( "{{*,0|*2,0},{0|v*},{^,^*|*,v}|*2,{0|*,*2},{^,^*|*,*2,0},{^|v*}}" ).canonical_form()), "{0|*2,{0|*,*2},{^,^*|*,*2,0},{^|v*}}") self.assertEqual( str( Game( "{{*,0|*,*2,0},{*,0|v},{^*|0}|{*,0|*,v},{*,^|v,v*},{*,v|0},{*2,0|*,0}}" ).canonical_form()), "{*,{*,0|*,*2,0},{^*|0}|{*,v|0}}") self.assertEqual( str( Game( "{{*,0|*,*2,0},{*,^|*,v},{0,^*|*,v},{^,^*|0,v*}|0,{*,0|0,^*},{^,^*|*,v}}" ).canonical_form()), "{{*,0|*,*2,0},{*,^|*,v},{0,^*|*,v},{^,^*|0,v*}|0}") self.assertEqual( str( Game( "{*,0,{*,^|*,0},{0,^*|*,0},{^,^*|*,v}|*,*2,*3,0,{*,^|0,v*},{0,^*|0,v*},{^,^*|*,v}}" ).canonical_form()), "{*,0,{*,^|*,0},{0,^*|*,0}|*,*2,*3,0}") self.assertEqual( str( Game("{{*,0|*,v},{*,^|v,v*},{0,^*|0,v*}|{*,v|0},{0,v*|0,v*}}"). canonical_form()), "{{*,0|*,v},{*,^|v,v*},{0,^*|0,v*}|0}") self.assertEqual( str( Game( "{*,0,{*,^|0,v*},{0,^*|*,0},{^,^*|v,v*}|*,{*,*2,0|*,0},{*,^|*,v},{*,^|0,v*}}" ).canonical_form()), "{0|*,{*,*2,0|*,0}}") self.assertEqual( str( Game( "{{*,0|v},{0,^*|*,v},{0|v*},{^,^*|0,v*}|0,{*,0|*,*2,0},{*2,0|0,v*}}" ).canonical_form()), "{*,0|*,0,{*,0|*,*2,0}}") self.assertEqual( str( Game( "{*,{*,0|*,*2,0},{0,^*|*,v},{0|v*},{^,^*|0,v*}|*,*2,0,{*,^|*,v},{0,^*|0,v*}}" ).canonical_form()), "{*,0,{*,0|*,*2,0}|*,*2,0}") self.assertEqual( str( Game( "{*,*2,{*,*2,0|*,v},{*,^|v,v*},{0,^*|0,v*}|{*,0|v},{0,^*|*,0},{0|*,*2},{^,^*|0,v*}}" ).canonical_form()), "0") self.assertEqual( str( Game( "{*2,{*,*2,0|*,v},{*,*2,0|0,v*},{^*|0}|0,{*,*2,0|*,0},{*,^|0,v*},{0,^*|0,v*},{^|*,0}}" ).canonical_form()), "{*2,{*,*2,0|*,v},{*,*2,0|0,v*},{^*|0}|0}") self.assertEqual( str( Game( "{0,{*,*2,0|0,v*},{*,0|*,*2,0},{*,^|*,v}|{*,0|0,^*},{^,^*|*,*2,0}}" ).canonical_form()), "{0|{*,0|0,^*},{^,^*|*,*2,0}}") self.assertEqual( str( Game("{{*,*2|v},{*,0|*,v},{^|0,v*}|*2,{*,*2|0},{^*|v},{^|*,0}}" ).canonical_form()), "{{*,*2|v},{*,0|*,v},{^|0,v*}|0}") self.assertEqual( str( Game("{{*,0|*,*2,0},{^,^*|0,v*}|{*,*2,0|*,0},{^*|v},{^|*,0}}"). canonical_form()), "{{*,0|*,*2,0},{^,^*|0,v*}|0}") self.assertEqual( str( Game( "{v,{*,*2,0|*,v},{^|0,v*}|*,{*,*2,0|*,v},{*,*2|0},{*,0|0,v*}}" ).canonical_form()), "{v,{*,*2,0|*,v},{^|0,v*}|0}") self.assertEqual( str( Game( "{{*,0|0,^*},{0,^*|v,v*}|*,*2,*3,{*,^|*,v},{0,^*|*,v},{0|v*}}" ).canonical_form()), "0") self.assertEqual( str( Game( "{*,0,{*,*2,0|0,v*},{*,0|*,*2,0},{0,^*|*,v}|{*,*2,0|*,0},{0|*2}}" ).canonical_form()), "{0|{*,*2,0|*,0},{0|*2}}")
def test_replace_reversible(self): # With return_change = False g = Game("{*|*}").replace_reversible() self.assertEqual(g, Game("0")) g = Game("{^,*|0}").replace_reversible() self.assertEqual(g, Game("^*")) # With return_change = True g, c = Game("{*|*}").replace_reversible(return_change=True) self.assertEqual(g, Game("0")) self.assertTrue(c) g, c = Game("{^,*|0}").replace_reversible(return_change=True) self.assertEqual(g, Game("^*")) self.assertTrue(c) g, c = Game().replace_reversible(return_change=True) self.assertEqual(g, Game()) self.assertFalse(c) g, c = Game("*").replace_reversible(return_change=True) self.assertEqual(g, Game("*")) self.assertFalse(c)
def test_remove_dominated(self): # With return_change = False g = Game("{0,1|0,1}").remove_dominated() self.assertEqual(g, Game("{1|0}")) g = Game("{*,0|-1,*,1}").remove_dominated() self.assertEqual(g, Game("{*,0|-1}")) g = Game("{0,*,-1,*|-1,0,-1}").remove_dominated() self.assertEqual(g, Game("{*,0|-1}")) # With return_change = True g, c = Game("{0,1|0,1}").remove_dominated(return_change=True) self.assertEqual(g, Game("{1|0}")) self.assertTrue(c) g, c = Game("{*,0|-1,*,1}").remove_dominated(return_change=True) self.assertEqual(g, Game("{*,0|-1}")) self.assertTrue(c) g, c = Game("{0,*,-1,*|-1,0,-1}").remove_dominated(return_change=True) self.assertEqual(g, Game("{*,0|-1}")) self.assertTrue(c) g, c = Game().remove_dominated(return_change=True) self.assertEqual(g, Game()) self.assertFalse(c) g, c = Game("*").remove_dominated(return_change=True) self.assertEqual(g, Game("*")) self.assertFalse(c)