Beispiel #1
0
class Thread(GameEvent):
    forum_id = db.Column(db.Integer, db.ForeignKey('forum.id'), nullable=False)
    posts = db.relationship('Post', backref='thread')
    title = db.Column(db.String(32))
    author_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    author = db.relationship("User")

    def __init__(self, forum_id, title, author_id):
        self.forum_id = forum_id
        self.title = title
        self.author_id = author_id

    def get_post_count(self):
        return len(self.posts)

    def get_most_recent_post(self):
        return Post.query.filter_by(thread_id=self.id).order_by(
            desc('time_created')).first()

    def get_author(self):
        return self.author.username

    def get_votes(self):
        """Return sum of all votes on all posts."""
        return sum([
            Upvote.query.filter_by(post_id=post.id, vote=1).count()
            for post in self.posts
        ])

    def get_views(self):
        """Return sum of all views on all posts."""
        raise NotImplementedError("We currently don't have this feature.")
Beispiel #2
0
class GameState(Base):
    __abstract__ = True

    id = db.Column(db.Integer,
                   primary_key=True,
                   autoincrement=True,
                   nullable=False)
    time_created = db.Column(db.DateTime, default=datetime.utcnow)
    time_modified = db.Column(db.DateTime,
                              default=datetime.utcnow,
                              onupdate=datetime.utcnow)
class Base(object):
    @declared_attr
    def __tablename__(cls):
        return to_var_name(cls.__name__)  # pluralize?

    __table_args__ = {'mysql_engine': 'InnoDB'}

    id = db.Column(db.Integer, primary_key=True)
Beispiel #4
0
class Session(GameEvent):
    time_logged_out = db.Column(db.DateTime)
    user_id = db.Column(db.Integer)
    county_day = db.Column(db.Integer)
    minutes = db.Column(db.Integer)

    def __init__(self, user_id, county_day):
        self.user_id = user_id
        self.county_day = county_day
        self.seconds = None
        self.valid = True

    def set_minutes(self):
        if self.time_logged_out == self.time_created:
            self.minutes = 0
        else:
            self.minutes = (self.time_logged_out -
                            self.time_created).seconds // 60
Beispiel #5
0
class BlacklistToken(GameState):
    """Token model for storing JWT tokens."""

    token = db.Column(db.String(500), unique=True, nullable=False)
    blacklisted_on = db.Column(db.DateTime, nullable=False)

    def __init__(self, token):
        self.token = token
        self.blacklisted_on = datetime.datetime.utcnow()

    @staticmethod
    def check_blacklist(auth_token):
        """Check whether an auth token has been blacklisted."""

        res = BlacklistToken.query.filter_by(token=str(auth_token)).first()
        if res:
            return True
        else:
            return False

    def __repr__(self):
        return f'<id: token: {self.token}'
Beispiel #6
0
class Post(GameEvent):
    thread_id = db.Column(db.Integer,
                          db.ForeignKey('thread.id'),
                          nullable=False)
    parent_post_id = db.Column(db.Integer)  # 0 if this is the parent
    title = db.Column(db.String(32))
    content = db.Column(db.String(5000))
    author_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    votes = db.Column(db.Integer)

    author = db.relationship("User")

    def __init__(self, thread_id, author_id, title, content, parent_post_id):
        self.thread_id = thread_id
        self.author_id = author_id
        self.title = title
        self.content = content
        self.parent_post_id = parent_post_id
        self.votes = 0

    def get_reply_count(self):
        replies = Post.query.filter_by(parent_post_id=self.id).all()
        return len(replies)

    def get_replies(self):
        return Post.query.filter_by(parent_post_id=self.id).all()

    def get_most_recent_reply(self):
        post = Post.query.filter_by(parent_post_id=self.id).order_by(
            desc('time_created')).first()
        if post is None:
            return self
        return post

    def get_author(self):
        return self.author.username

    def get_votes(self):
        return Upvote.query.filter_by(post_id=self.id, vote=1).count()

    def get_vote_status(self, user_id):
        """Return 1 for voted and 0 for not voted.

        If no vote exists then vote is 0.
        This uses integers rather than booleans because it makes math easier.
        """
        vote = Upvote.query.filter_by(post_id=self.id, user_id=user_id).first()
        try:
            return vote.vote
        except AttributeError:
            return 0
Beispiel #7
0
class DAU(GameEvent):
    # User data
    user_id = db.Column(db.Integer)
    county_id = db.Column(db.Integer)
    account_age_in_days = db.Column(db.Integer)
    sessions = db.Column(db.Integer)
    minutes_played = db.Column(db.Integer)
    ads_watched = db.Column(db.Integer)
    # Game data
    world_day = db.Column(db.Integer)
    county_day = db.Column(db.Integer)
    score = db.Column(db.Integer)
    land = db.Column(db.Integer)
    population = db.Column(db.Integer)
    happiness = db.Column(db.Integer)
    healthiness = db.Column(db.Integer)
    # Resources
    gold = db.Column(db.Integer)
    wood = db.Column(db.Integer)
    iron = db.Column(db.Integer)
    stone = db.Column(db.Integer)
    research = db.Column(db.Integer)
    mana = db.Column(db.Integer)
    grain = db.Column(db.Integer)
    lifetime_gold = db.Column(db.Integer)
    lifetime_wood = db.Column(db.Integer)
    lifetime_iron = db.Column(db.Integer)
    lifetime_stone = db.Column(db.Integer)
    lifetime_research = db.Column(db.Integer)
    lifetime_mana = db.Column(db.Integer)
    # Choices
    production_choice = db.Column(db.Integer)
    research_choice = db.Column(db.String(128))
    rations = db.Column(db.Float)
    taxes = db.Column(db.Integer)
    technologies = db.Column(db.Integer)
    # Military data
    peasant = db.Column(db.Integer)
    soldier = db.Column(db.Integer)
    archer = db.Column(db.Integer)
    besieger = db.Column(db.Integer)
    summon = db.Column(db.Integer)
    elite = db.Column(db.Integer)
    monster = db.Column(db.Integer)
    maximum_offense = db.Column(db.Integer)
    maximum_defence = db.Column(db.Integer)
    # Building Data
    house = db.Column(db.Integer)
    field = db.Column(db.Integer)
    pasture = db.Column(db.Integer)
    mill = db.Column(db.Integer)
    mine = db.Column(db.Integer)
    fort = db.Column(db.Integer)
    stables = db.Column(db.Integer)
    bank = db.Column(db.Integer)
    tavern = db.Column(db.Integer)
    tower = db.Column(db.Integer)
    lab = db.Column(db.Integer)
    arcane = db.Column(db.Integer)
    quarry = db.Column(db.Integer)
    lair = db.Column(db.Integer)

    def __init__(self, user_id):
        self.user_id = user_id
        self.update_self(user_id)
        self.sessions = self.get_sessions(user_id)
        self.minutes_played = self.get_minutes_played(user_id)
        self.ads_watched = 0

    def update_self(self, user_id):
        user = User.query.get(user_id)
        county = user.county
        infrastructure = county.infrastructure

        self.world_day = county.kingdom.world.day
        self.county_day = county.day
        self.account_age_in_days = (datetime.utcnow() - user.time_created).days
        self.county_id = county.id
        self.land = county.land
        self.population = county.population
        self.gold = county.gold
        self.wood = county.wood
        self.iron = county.iron
        self.stone = county.stone
        self.research = county.research
        self.mana = county.mana
        self.grain = county.grain_stores
        self.lifetime_gold = county.lifetime_gold
        self.lifetime_wood = county.lifetime_wood
        self.lifetime_iron = county.lifetime_iron
        self.lifetime_stone = county.lifetime_stone
        self.lifetime_research = county.lifetime_research
        self.lifetime_mana = county.lifetime_mana
        self.happiness = county.happiness
        self.healthiness = county.healthiness

        self.production_choice = county.production_choice
        try:
            self.research_choice = county.research_choice.name
        except AttributeError:
            self.research_choice = None
        self.rations = county.rations
        self.taxes = county.tax_rate
        self.technologies = len(county.technologies)

        self.peasant = county.armies['peasant'].total
        self.soldier = county.armies['soldier'].total
        self.archer = county.armies['archer'].total
        self.besieger = county.armies['besieger'].total
        self.summon = county.armies['summon'].total
        self.elite = county.armies['elite'].total
        self.monster = county.armies['monster'].total
        self.maximum_offense = county.get_offensive_strength(scoreboard=True)
        self.maximum_defence = county.get_defensive_strength()

        self.house = infrastructure.buildings['house'].total
        self.field = infrastructure.buildings['field'].total
        self.pasture = infrastructure.buildings['pasture'].total
        self.mill = infrastructure.buildings['mill'].total
        self.mine = infrastructure.buildings['mine'].total
        self.fort = infrastructure.buildings['fort'].total
        self.stables = infrastructure.buildings['stables'].total
        self.bank = infrastructure.buildings['bank'].total
        self.tavern = infrastructure.buildings['tavern'].total
        self.tower = infrastructure.buildings['tower'].total
        self.lab = infrastructure.buildings['lab'].total
        self.arcane = infrastructure.buildings['arcane'].total
        self.quarry = infrastructure.buildings['quarry'].total
        self.lair = infrastructure.buildings['lair'].total

    @staticmethod
    def get_sessions(user_id):
        time_cutoff = datetime.utcnow() - timedelta(hours=1)
        return Session.query.filter_by(user_id=user_id).filter(
            Session.time_logged_out > time_cutoff).count()

    @staticmethod
    def get_minutes_played(user_id):
        time_cutoff = datetime.utcnow() - timedelta(hours=1)
        sessions = Session.query.filter_by(user_id=user_id).filter(
            Session.time_logged_out > time_cutoff).all()
        return sum(session.minutes for session in sessions
                   if session.minutes is not None)
Beispiel #8
0
class Achievement(GameEvent):

    user_id = db.Column(db.Integer,
                        db.ForeignKey('user.id', ondelete="CASCADE"),
                        nullable=False)
    name = db.Column(db.String(64))  # Unique name for mapping purposes
    display_title = db.Column(
        db.String(64))  # The name of the achievement the user sees
    category = db.Column(
        db.String(64))  # A way to group it for coding purposes
    sub_category = db.Column(
        db.String(64))  # A way to identify it from its group
    description = db.Column(db.String(64))  # For the user to read
    current_tier = db.Column(db.Integer)  # What level they are on. Starts at 0
    maximum_tier = db.Column(db.Integer)  # The highest level they can reach
    points_rewarded = db.Column(db.Integer)  # Points rewarded at each level

    tier1 = db.Column(db.Integer)
    tier2 = db.Column(db.Integer)
    tier3 = db.Column(db.Integer)
    tier4 = db.Column(db.Integer)
    tier5 = db.Column(db.Integer)

    def __init__(self,
                 name,
                 display_title=None,
                 category=None,
                 sub_category=None,
                 description=None,
                 points_rewarded=0,
                 maximum_tier=0,
                 tier1=None,
                 tier2=None,
                 tier3=None,
                 tier4=None,
                 tier5=None):
        self.name = name
        self.display_title = display_title
        self.category = category
        self.sub_category = sub_category
        self.description = description
        self.current_tier = 0
        self.maximum_tier = maximum_tier
        self.points_rewarded = points_rewarded

        self.tier1 = tier1
        self.tier2 = tier2
        self.tier3 = tier3
        self.tier4 = tier4
        self.tier5 = tier5

    def get_earned_required_amount_message(self):
        if self.current_tier == 0:
            return "You have made no progress on this achievement."
        return f"Last reward at {getattr(self, 'tier' + str(self.current_tier))} {self.name}."

    def get_next_required_amount_message(self):
        if self.current_tier == self.maximum_tier:
            return "You have unlocked all tiers."
        return f"Next reward at {getattr(self, 'tier' + str(self.current_tier + 1))} {self.name}."
Beispiel #9
0
class Army(GameState):
    # These would work better as integers but I'd have to modify a lot of code.
    PEASANT = 0
    SOLDIER = 1
    ARCHER = 2
    BESIEGER = 3
    ELITE = 4
    MONSTER = 5
    SUMMON = 6
    TYPES = dict(peasant=PEASANT,
                 soldier=SOLDIER,
                 archer=ARCHER,
                 besieger=BESIEGER,
                 elite=ELITE,
                 monster=MONSTER,
                 summon=SUMMON)

    county_id = db.Column(db.Integer,
                          db.ForeignKey('county.id', ondelete="CASCADE"),
                          nullable=False)
    name = db.Column(db.String(64))
    class_name = db.Column(db.String(64))
    class_name_plural = db.Column(db.String(64))
    total = db.Column(db.Integer)
    traveling = db.Column(db.Integer)
    currently_training = db.Column(db.Integer)
    trainable_per_day = db.Column(db.Integer)
    gold = db.Column(db.Integer)
    wood = db.Column(db.Integer)
    iron = db.Column(db.Integer)
    upkeep = db.Column(db.Integer)
    category = db.Column(db.String(32))
    attack = db.Column(db.Integer)
    defence = db.Column(db.Integer)
    _health = db.Column(db.Integer)
    armour_type = db.Column(db.String(32))
    description = db.Column(db.String(128))
    ability = db.Column(db.String(32))
    ability_description = db.Column(db.String(128))

    @property
    def type(self):
        """Return an int representing the unit type.

        This will later allow comparison with constants such as:
        if unit.type == unit.BESIEGER:
           do_something_to_siege_units()

        This helps prevent typos.
        """
        return self.TYPES[self.name]

    @property
    def key(self):
        return self.name

    def __init__(self, name, class_name, class_name_plural, total,
                 trainable_per_day, gold, wood, iron, upkeep, category, attack,
                 defence, health, armour_type, description, ability,
                 ability_description):
        self.name = name
        self.class_name = class_name
        self.class_name_plural = class_name_plural
        self.total = total
        self.traveling = 0
        self.currently_training = 0
        self.trainable_per_day = trainable_per_day  # Number than can train per game-day
        self.gold = gold
        self.wood = wood
        self.iron = iron
        self.upkeep = upkeep
        self.category = category
        self.attack = attack
        self.defence = defence
        self.health = health
        self.armour_type = armour_type
        self.description = description
        self.ability = ability
        self.ability_description = ability_description

    @property
    def available(self):
        return self.total - self.traveling

    @property
    def health(self):
        """Health can't go below 0."""
        return max(self._health, 1)

    @health.setter
    def health(self, value):
        self._health = value
Beispiel #10
0
class World(GameState):
    kingdoms = db.relationship('Kingdom', backref='world')
    age = db.Column(db.Integer)  # How many 'reset events'
    day = db.Column(db.Integer)  # How many in-game days have passed this age
    season = db.Column(db.String(16))  # The current season

    def __init__(self):
        self.age = 1
        self.day = 0
        self.season = seasons[0]

    def advance_day(self):
        if self.day >= 0:
            for county in County.query.all():
                if county.user.is_bot:
                    county.temporary_bot_tweaks()
                else:
                    county.advance_day()
            for kingdom in Kingdom.query.all():
                kingdom.advance_day()

        self.day += 1
        if self.day % 36 == 0:  # Every 36 game days we advance the game season
            season_index = seasons.index(self.season) + 1
            if season_index == len(seasons):
                season_index = 0
            self.season = seasons[season_index]

    def advance_analytics(self):
        if self.day < 0:
            return
        users = User.query.filter_by(is_bot=False).filter(
            User.county != None).all()
        for user in users:
            # First check and set their retention
            user_age = (datetime.utcnow() - user.time_created).days
            if user.get_last_logout() and user.get_last_logout().date(
            ) == datetime.today().date():
                retention = 1
            else:
                retention = 0
                user.day1_retention = 1
            if user_age == 1:
                user.day1_retention = retention
            elif user_age == 3:
                user.day3_retention = retention
            elif user_age == 7:
                user.day7_retention = retention
            # If they are playing this age, create a DAU for them
            if user.county:
                dau_event = DAU(user.id)
                dau_event.save()
        self.export_data_to_csv()

    def advance_age(self):
        kingdoms = Kingdom.query.all()
        counties = County.query.all()
        users = User.query.all()
        for user in users:
            if user.county:
                user.ages_completed += 1
        for kingdom in kingdoms:
            kingdom.leader = 0
            kingdom.wars_total_ta = 0
            kingdom.wars_won_ta = 0
            kingdom.approval_rating = Kingdom.BASE_APPROVAL_RATING
            kingdom.save()
        winning_kingdoms = [
            sorted(kingdoms, key=lambda x: x.wars_won_ta, reverse=True)[0],
            sorted(kingdoms,
                   key=lambda x: x.total_land_of_top_three_counties,
                   reverse=True)[0]
        ]
        winning_county = sorted(counties, key=lambda x: x.land,
                                reverse=True)[0]
        for kingdom in winning_kingdoms:
            for county in kingdom.counties:
                county.user.gems += 1
        winning_county.user.gems += 1

        tables = [
            'DAU', 'army', 'building', 'casting', 'chatroom', 'diplomacy',
            'economy', 'espionage', 'expedition', 'infiltration',
            'infrastructure', 'magic', 'message', 'military', 'notification',
            'preferences', 'scientist', 'session', 'technology_to_technology',
            'technology', 'trade', 'transaction', 'wizardry', 'county'
        ]
        table_mixers.drop_then_rebuild_tables(db, tables)
        self.age += 1
        self.day = -24

    def export_data_to_csv(self):
        all_tables = []
        table_name_reference = []
        tables = db.metadata.tables
        table_names = db.engine.table_names()
        for name in table_names:
            # Each table is a list inside the list
            new_table = []
            table_name_reference.append(name)
            if name not in {}:
                # Each row will be a list inside the table list, inside the all_tables list
                table = tables[name]
                header_row = []
                for column in table.columns:
                    header_row.append(column.name)
                new_table.append(header_row)
                for row in db.session.query(table).all():
                    normal_row = []
                    for value in row:
                        normal_row.append(value)
                    new_table.append(normal_row)
            all_tables.append(new_table)
        # We have a list of smaller lists. Each smaller list should be a csv file (each one is a separate sql table)
        current_path = f"export/age-{self.age}"
        if not os.path.exists(current_path):
            os.makedirs(current_path)

        for index, table in enumerate(all_tables):
            if len(table) < 2:
                continue
            headers = table.pop(0)
            name = table_name_reference[index]

            filename = f"{current_path}/{name}_table.csv"
            df = DataFrame(table)
            df.to_csv(filename, header=headers)

    def __repr__(self):
        return '<World %r (%r)>' % ('id:', self.id)
Beispiel #11
0
class GameEvent(Base):
    __abstract__ = True

    id = db.Column(db.Integer, primary_key=True)
    time_created = db.Column(db.DateTime, default=datetime.utcnow)