Exemple #1
0
    def open_deck(self,
                  f,
                  dname=None,
                  durl=None,
                  aname=None,
                  aurl=None,
                  diurl=None):
        """
        Opens a deck from file f (overwriting current deck if present)
        :param f: the file path (local)
        :param dname: name of the deck
        :param durl: deck url
        :param aname: deck author
        :param aurl: author url
        :param diurl: discord url
        """
        # TODO: add better exception handling and raising i.e. check for existence
        #  of file first
        if not os.path.exists(f):
            raise lts.LituusException(lts.EIOIN,
                                      "File {} does not exist".format(f))

        try:
            self._read_deck_(f)
            self._dname = dname if dname else self._path.split('/')[-1]
            self._url = durl if durl else "Unknown"
            self._author = aname if aname else "Unknown"
            self._aurl = aurl if aurl else ""
            self._diurl = diurl if diurl else ""
        except IOError as e:
            raise lts.LituusExeption(lts.EIOIN, e)
        except Exception as e:
            raise lts.LituusException(lts.EUNDEF, e)
Exemple #2
0
def merge_attrs(attrs, strict=1):
    """
     merges the attributes lists in attrs based on specified strictness level.
     if strict is True confirms all proplists have
     the same key->value pairs, otherwise adds unique key->value pairs and 'ands'
     differing values
    :param attrs: list of attribute lists
    :param strict: oneof:
      0 = LOW no checking on sameness across parameters/parameter values
      1 = MEDIUM parameter values across common parameters must be the same but
       parameters are not required to be shared across all attribute dicts
      2 = HIGH parameters and parameter values must be the same in each prop list
    :return: merged attribute dicts
    """
    if not attrs: return {}  # don't bother if the list is empty
    mattrs = {}
    keys = list(set.union(*map(set, [attr.keys() for attr in attrs])))
    for key in keys:
        vals = set()

        # check each paraemter for existence if high strictness
        for attr in attrs:
            if not key in attr:
                if strict == 2:
                    raise lts.LituusException(
                        lts.ETAG, "Incompatible attribute {}".format(key))
            else:
                vals.add(attr[key])

        # check for same attribute values if strictness is not low
        if strict > 0 and len(vals) > 1:
            raise lts.LituusException(
                lts.ETAG, "Incompatible attribute values {}".format(key))
        mattrs[key] = mtgl.AND.join([val for val in vals])
    return mattrs
Exemple #3
0
 def attr(self, nid, attr):
     try:
         return self._t.node[nid][attr]
     except KeyError:
         if not nid in self._t:
             raise lts.LituusException(lts.ENODE,
                                       "No such node {}".format(nid))
         else:
             raise lts.LituusException(
                 lts.ENODE, "{} has attribute {}".format(nid, attr))
Exemple #4
0
    def _read_deck_(self, f):
        """ reads a deck from file with path f """
        # check for commander
        if not self._cmdr: raise
        ds = None

        try:
            # get the multiverse (assumes saved)
            mv = multiverse.multiverse(0)

            # check file extension and read in if possible
            _, fext = os.path.splitext(f)
            if fext == '.cod': ds = EDHDeck._read_cod_(f)
            elif fext == '.dec': ds = EDHDeck._read_dec_(f)
            else:
                raise lts.LituusException(
                    lts.EPARAM, "Unable to process '{}' files".format(fext))
        except lts.LituusException:
            raise
        except Exception as e:
            raise lts.LituusException(lts.EUNDEF,
                                      "Error reading deck {}\n{}".format(f, e))

        # start with the mainboard (make sure split cards are handled properly)
        for qty, cname in ds['mainboard']:
            if '/' in cname and not '//' in cname:
                cname = " // ".join(
                    [cname.strip() for cname in cname.split('/')])
            self.add_card(mv[cname], qty)

        # then the sideboard
        for qty, cname in ds['sideboard']:
            if '/' in cname and not '//' in cname:
                cname = " // ".join(
                    [cname.strip() for cname in cname.split('/')])
            try:
                self.add_sb_card(mv[cname], qty)
            except mtgl.MTGLException:
                # we'll ignore sideboard errors
                pass

        # since some decks have commander(s) in sideboard, some in mainboard & some
        # in both, make uniform and place in mainboard
        for cmdr in self._cmdr:
            if cmdr in self._sb:
                if not cmdr in self._mb: self.add_card(mv[cmdr], 1)
                self.del_sb_card(cmdr)

        # set the deck name and path
        self._dname = ds['name']  # set the name
        self._path = f
Exemple #5
0
 def siblings(self, nid):
     try:
         ss = self.children(self.parent(nid))
         ss.remove(nid)
         return ss
     except KeyError:
         raise lts.LituusException(lts.ENODE, "No such node {}".format(nid))
Exemple #6
0
 def parent(self, nid):
     try:
         return next(self._t.predecessors(nid))
     except StopIteration:
         return None
     except KeyError:
         raise lts.LituusException(lts.ENODE, "No such node {}".format(nid))
Exemple #7
0
    def _read_dec_(f):
        """ reads a .dec deck file """
        ds = {}

        fin = None
        try:
            fin = open(f)
            ls = fin.readlines()
            fin.close()

            # get cards from mainboard and sideboard
            # 3 cases mainboard, sideboard and token (we ignore tokens for now)
            ds = {'name': "", 'mainboard': [], 'sideboard': []}
            sb = False
            for l in ls:
                l = l.strip()  # remove trailing newlines
                if not l: continue  # skip empty lines
                if l.startswith('SB:'):
                    sb = True  # tokens start after SB and are not prepended with "SB: "
                if sb and not l.startswith('SB:'): break
                else:
                    if sb:
                        ds['sideboard'].append(
                            EDHDeck._dec_card_(l[3:].strip()))
                    else:
                        ds['mainboard'].append(EDHDeck._dec_card_(l))
        except IOError as e:
            raise lts.LituusException(lts.EOIOIN,
                                      "Failed reading file {}".format(e))
        finally:
            if fin: fin.close()
        return ds
Exemple #8
0
def harvest(name, jcard):
    """
     extract details from the json card and return the card dict
    :param name: the name of the card
    :param jcard: json card dict
    :return: card dict
    """
    try:
        dcard = {
            'rid':
            md5(name.encode()).hexdigest(),
            'name':
            name,
            'layout':
            jcard['layout'],
            'super-type':
            jcard['supertypes'],
            'type':
            jcard['types'],
            'sub-type':
            jcard['subtypes'],
            'cmc':
            int(jcard['convertedManaCost'])
            if 'convertedManaCost' in jcard else 0,
            'mana-cost':
            jcard['manaCost'] if 'manaCost' in jcard else '{0}',
            'P/T':
            None,
            'loyalty':
            None,
            'color-ident':
            jcard['colorIdentity'],
            'colors':
            jcard['colors'],
            'oracle':
            jcard['text'] if 'text' in jcard else "",
            'tag':
            "",
            'mtgt':
            None,
            'sets':
            jcard['printings'],
        }
    except KeyError as e:
        raise lts.LituusException(lts.EDATA, "{}->{}".format(name, e))

    # check for existence before setting the following
    if 'Planeswalker' in jcard['types'] and 'loyalty' in jcard:
        dcard['loyalty'] = jcard['loyalty']
    elif 'Creature' in jcard['types']:
        dcard['P/T'] = "{}/{}".format(jcard['power'], jcard['toughness'])
    if 'faceConvertedManacost' in jcard:
        dcard['face-cmc'] = jcard['faceConvertedManacost']
    else:
        dcard['face-cmc'] = dcard['cmc']

    return dcard
Exemple #9
0
 def right_sibling(self, nid):
     try:
         ss = self.children(self.parent(nid))
         i = ss.index(nid)
         return ss[i + 1]
     except IndexError:
         return None
     except KeyError:
         raise lts.LituusException(lts.ENODE, "No such node {}".format(nid))
Exemple #10
0
    def _read_cod_(f):
        """ reads a cockatrice deck file """
        ds = {'name': "", 'mainboard': [], 'sideboard': []}
        fin = None
        try:
            # open the xml file, soup it and close
            fin = open(f)
            deck = soup(fin, 'xml')
            fin.close()

            # is there a name in the cockatrice file?
            try:
                dname = deck.find('deckname').contents[0].__str__()
                if dname: ds['name'] = dname
            except IndexError:
                pass

            # get mainboard, sideboard cards
            try:
                zones = deck.find_all('zone')
                for zone in zones:
                    if not 'name' in zone.attrs: continue
                    if zone.attrs['name'] == 'main':
                        for card in zone.find_all('card'):
                            ds['mainboard'].append((
                                int(card.attrs['number']),  # qty
                                card.attrs['name'].__str__())  # card name
                                                   )
                    elif zone.attrs['name'] == 'side':
                        for card in zone.find_all('card'):
                            ds['sideboard'].append((
                                int(card.attrs['number']),  # qty
                                card.attrs['name'].__str__())  # card name
                                                   )
            except IndexError as e:
                raise lts.LituusException(lts.EDATA, "Unexpected {}".format(e))
        except IOError as e:
            raise lts.LituusException(lts.EOIOIN,
                                      "Failed reading file {}".format(e))
        finally:
            if fin: fin.close()

        # set the path if we get here
        return ds
Exemple #11
0
 def add_edge(self, pid, cid):
     """
      adds an edge from parent pid to child cid
     :param pid: parent-id
     :param cid: child-id
     """
     # don't allow edges to be added to a node with a parent
     if self.parent(cid):
         raise lts.LituusException(lts.ENODE,
                                   "{} is not rootless".format(cid))
     self._t.add_edge(pid, cid)
Exemple #12
0
 def del_card(self, cname):
     """
      deletes the card identified by cname
     :param cname: card name
     """
     try:
         del self._qty[cname]
         del self._mb[cname]
     except KeyError:
         raise lts.LituusException(lts.EPARAM,
                                   "No such card {}".format(cname))
Exemple #13
0
 def del_sb_card(self, cname):
     """
     deletes the sideboard card identified by cname
     :param cname: card name
     """
     try:
         del self._sqty[cname]
         del self._sb[cname]
     except KeyError:
         raise lts.LituusException(lts.EDATA,
                                   "{} does not exist".format(cname))
Exemple #14
0
 def add_attr(self, nid, k, v):
     """
      adds the k=v pair to the nodes attributes. Note if the attribute k
      already exists in the node's attributes, it will be overwritten with v
     :param nid: the node-id
     :param k: the key
     :param v: the value
     """
     try:
         self._t.node[nid][k] = v
     except KeyError:
         raise lts.LituusException(lts.ENODE, "No such node {}".format(nid))
Exemple #15
0
def untag(tkn):
    """
     returns the tag-id, tag value and attribute dict of tkn if it is a tagged item
    :param tkn: the token to untag
    :return: the tag, tag-value and property dict
    """
    attrs = {}
    try:
        # get the tag, the value and any properties
        tag, val, attr = re_tag.match(tkn).groups()
        if attr:
            attrs = {
                p[0]: p[1]
                for p in [p.split('=') for p in re_tag_attrs.findall(tkn)]
            }
        return tag, val, attrs
    except AttributeError:  # signifies a None returned from re_tag
        raise lts.LituusException(lts.ETAG, "Invalid tag {}".format(tkn))
Exemple #16
0
    def cedh_hash(self):
        """
        calculates the cockatrice hash for an online cEDH Competition deck
        (Commander(s) and only commander(s) in sidedeck)
        """
        # we have to temporalily remove any cards in the sideboard
        t_sb = self._sb.copy()
        t_sqty = self._sqty.copy()
        t_mb = self._mb.copy()
        t_qty = self._qty.copy()
        dhash = None

        try:
            # empty the sideboard
            self.del_sideboard()

            # move commanders from mainboard to sideboard before hashing
            for cmdr in self.commander:
                card = self._mb[cmdr]
                self.del_card(cmdr)
                self.add_sb_card(card, 1)

            # calculate the hash
            dhash = self.hash(True)

            # & restore the deck
            for cname in self._sb:
                self.add_card(self._sb[cname], 1)
            self.del_sideboard()
            for cname in t_sb:
                self.add_sb_card(t_sb[cname], t_sqty[cname])
        except Exception as e:
            # recover
            self._mb = t_mb
            self._qty = t_qty
            self._sb = t_sb
            self._sqty = t_sqty
            raise lts.LituusException(lts.EUNDEF, "Error hashing {}".format(e))

        return dhash
Exemple #17
0
    def gold_hist(self):
        """
         returns a histogram of the count of multicolored cards. This will be of
         the form i->n where i is the number of colors and n is the number of cards
         in the deck having i colors
        """
        # set histogram to 0 for 2..n where n is the maximum number of colors
        # i.e. 2 = 2 different colors, 3 = 3 different colors etc
        mch = {x: 0 for x in range(2, len(mtg.mana_colors) + 1)}

        for cname in self._mb:
            # get card object, skip if land
            card = self._mb[cname]
            if card.is_land(): continue

            # get the mana symbols in the cards cost
            try:
                mcs = mtg.re_mana_sym.findall(card.mana_cost)
            except TypeError:
                # cards with no casting cost i.e. suspend will end up here
                if mv[card].cmc == 0: continue
                else:
                    raise lts.LituusException(lts.UNDEF,
                                              "Unexpected {}".format(e))
            mcs2 = []

            # for ease, recreate manacost list removing could be snow, phyrexian
            # or hybrid and numeral symbols
            for mc in mcs:
                if '/' in mc:
                    for s in mc.split('/'):
                        if s in mtg.mana_colors: mcs2.append(s)
                elif mc in mtg.mana_colors: mcs2.append(mc)

            try:
                mch[len(set(mcs2))] += 1
            except KeyError:
                pass
        return mch
Exemple #18
0
 def findall(self, ntype, source='root', attr=None, val=None):
     """
      finds all nodes in the tree of the type ntype starting at source with
      attribute attr (if set) having value val (if set)
     :param ntype: node type to find
     :param source: the source to start the search from
     :param attr: the attribute key the node will have
     :param val: the val that the given attribute key will have
     :return: a list of node ids
     """
     if val and not attr:
         raise lts.LituusException(lts.ETREE, "attr required with val")
     found = []
     for node in nx.dfs_preorder_nodes(self._t, source):
         if node_type(node) == ntype and not node == source:
             if attr:
                 if attr in self._t.node[node]:
                     if val:
                         if self._t.node[node][attr] == val:
                             found.append(node)
                     else:
                         if attr in self._t.node[nid]: found.append(node)
             else: found.append(node)
     return found
Exemple #19
0
    def double_mana(self):
        """
        returns list of cards in pack with double (or more) of the same mana symbol
        in the casting cost
        """
        dbl = []
        for cname in self._mb:
            # get the card object and skip if a land
            card = self._mb[cname]
            if card.is_land(): continue

            # find mana symbols in the casting cost
            try:
                mcs = mtg.re_mana_sym.findall(card.mana_cost)
            except TypeError as e:
                # cards with no casting cost i.e. suspend will end up here
                if mv[card].cmc == 0: continue
                else:
                    raise lts.LituusException(lts.UNDEF,
                                              "Unexpected {}".format(e))

            # for ease, recreate manacost list removing extra symbols could be snow,
            # phyrexian or hybrid and numeral symbols
            mcs2 = []
            for mc in mcs:
                if '/' in mc:
                    for s in mc.split('/'):
                        if s in mtg.mana_colors: mcs2.append(s)
                elif mc in mtg.mana_colors: mcs2.append(mc)

            # iterate the modified list of mana symbols
            for mc in mcs2:
                if mcs2.count(mc) > 1:
                    dbl.append(cname)
                    break
        return dbl
Exemple #20
0
    def adds_mana_all(self): raise lts.LituusException(lts.EIMPL,"Pending")

    def adds_mana(self): raise lts.LituusException(lts.EIMPL,"Pending")
Exemple #21
0
    def land_category(self): raise lts.LituusException(lts.EIMPL,"Pending")

    #### PRIVATE HELPER FUNCTIONS ####

    def _etb_self_(self): raise lts.LituusException(lts.EIMPL,"Pending")
Exemple #22
0
    def mana_plurality(self): raise lts.LituusException(lts.EIMPL,"Pending")

    #### LAND RELATED ####

    def utility_land(self): raise lts.LituusException(lts.EIMPL,"Pending")
Exemple #23
0
    def adds_mana_pref(self): raise lts.LituusException(lts.EIMPL,"Pending")

    def mana_plurality(self): raise lts.LituusException(lts.EIMPL,"Pending")
Exemple #24
0
    def utility_land(self): raise lts.LituusException(lts.EIMPL,"Pending")

    def nonmana_land(self): raise lts.LituusException(lts.EIMPL,"Pending")
Exemple #25
0
 def save_deck(self, f):
     raise lts.LituusException(lts.EIMPL, "Pending")
Exemple #26
0
    def nonmana_land(self): raise lts.LituusException(lts.EIMPL,"Pending")

    def land_category(self): raise lts.LituusException(lts.EIMPL,"Pending")
Exemple #27
0
    def _etb_self_(self): raise lts.LituusException(lts.EIMPL,"Pending")

    def _etb_other_(self): raise lts.LituusException(lts.EIMPL,"Pending")
Exemple #28
0
    def _etb_other_(self): raise lts.LituusException(lts.EIMPL,"Pending")

    def _reduce_by_(self,ms): raise lts.LituusException(lts.EIMPL,"Pending")
Exemple #29
0
def multiverse(update=0):
    """
     :param update: one of
        0 = load saved multiverse
        1 = reparse json file and create new multiverse
        2 = download json file and create new multiverse
      https://mtgjson.com/json/AllSets.json 
     :returns multiverse dict
    """
    # files to create
    mv = pack.Pack()  # multiverse
    tc = {}  # transformed cards
    n2r = {}  # name to reference dict

    if update == 0:
        fin = None
        try:
            fin = open(mvpath, 'rb')
            mv = pickle.load(fin)
            fin.close()
            return mv
        except FileNotFoundError:
            raise lts.LituusException(lts.EIOIN,
                                      "Multiverse file does not exist")
        except pickle.PickleError:
            raise lts.LituusException(lts.EIOIN, "Error loading multiverse")
        finally:
            if fin: fin.close()

    # there is no version checking. on update, downloads AllCards.json & reparses
    # TODO: Downloading allcards disabled until debugging is complete
    if update == 2 and False:
        fout = None
        try:
            print("Requesting AllCards.json")
            jurl = requests.get(url_cards)
            if jurl.status_code != 200: raise RuntimeError
            fout = open(jpath, 'w')
            fout.write(jurl.json())
            fout.close()
            print("AllCards.json updated")
        except RuntimeError:
            raise lts.LituusException(lts.ENET,
                                      "Failed to download AllCards.json")
        except OSError:
            raise lts.LituusException(lts.EIOOUT,
                                      "Failed to save AllCards.json")
        finally:
            if fout: fout.close()

    # read in AllCards.json
    fin = None
    try:
        print("Loading the Multiverse")
        fin = open(jpath, 'r')
        mverse = _hack_cards_(json.load(fin))  # fix errors in cards
        fin.close()
    except IOError:
        raise lts.LituusException(lts.ERRIOIN, "Error reading AllCards.json")
    finally:
        if fin: fin.close()

    # parse the mverse
    print('Tagging the Multiverse')
    start = time.time()
    import_cards(mv, tc, n2r, mverse)
    end = time.time()
    print("Imported {} cards and {} transformed cards in {:.2f}s.".format(
        mv.qty(), len(tc), end - start))
    #TODO: add graphing here and do the progress bar
    print('Graphing the Multiverse')

    # pickle the multiverse
    fout = None
    try:
        print("Writing multiverse file")
        fout = open(mvpath, 'wb')
        pickle.dump(mv, fout)
        fout.close()
    except pickle.PickleError:
        raise lts.LituusException(lts.EIOOUT, "Failed pickling multiverse")
    except IOError as e:
        raise lts.LituusException(lts.EIOOUT, "Failed saving multiverse")
    finally:
        if fout: fout.close()

    # & then transformed
    fout = None
    try:
        print("Writing transformed file")
        fout = open(tcpath, 'wb')
        pickle.dump(tc, fout)
        fout.close()
    except pickle.PickleError as e:
        raise lts.LituusException(lts.EIOOUT, "Failed pickling transformed")
    except IOError as e:
        raise lts.LituusException(lts.EIOOUT, "Failed saving transformed")
    finally:
        if fout: fout.close()

    # & then the n2r dict
    fout = None
    try:
        print("Writing Name Reference file")
        fout = open(n2rpath, 'wb')
        pickle.dump(n2r, fout)
        fout.close()
    except pickle.PickleError as e:
        raise lts.LituusException(lts.EIOOUT, "Failed pickling name reference")
    except IOError as e:
        raise lts.LituusException(lts.EIOOUT, "Failed saving name reference")
    finally:
        if fout: fout.close()

    return mv
Exemple #30
0
    def print(self,attr=False): raise lts.LituusException(lts.EIMPL,"Pending")

    @property