Table of Contents
CSE 30332 - HW2

CSE 30332 - HW2

Programming Paradigms


In this assignment you will be using object oriented programming tools and principles in python. Building on top of HW1 you will be using these tools to wrap your Reddit data in objects.

Command Line Arguments


The reddit_browser.py script takes the following arguments:


    --subreddits SUB1,SUB2,SUB3,...SUBN   The list of subreddits to scrape, delimited by commas (default: 'ultimate,cats,houseplants')
    --num        LIMIT                    Number of subreddits, titles, and comments to display (default: 5)
    --regex      REGEX                    A regex to use to filter subreddits, post titles, and comments (default: '.*')
    --attr       ATTR                     Field to use when printing comments by (default: 'score')
    --reverse                             Include this flag to reverse the output
                        

The --subreddits flag specifies the list of subreddits to scrape, is comma delimited, and can be of variable length, default is 'ultimate,cats,houseplants'

The --num flag specifies the number of titles to display per subreddit, the default is 5.

The --regex flag specifies the regex used to filter the subreddits, post titles, and comments, default is '.*'.

The --attr flag specifies the field to use when printing the comments, default is 'score'.

The --reverse flag specifies whether the output should be printed in reverse direction or not.

Code Overview and Scaffold


reddit_classes.py

import requests
import re
import os
from typing import Generator

class Browser:
    level = 0
    input = None

    def __init__(self, args: dict):
    '''Parse the argument dictionary to prepare the Browser class for use.

    Args:
        args (dict): A dictionary of the arguments passed into the code at run time.

    Returns:
        None
    '''

        num = args['num']

    def run(self):
    '''Using an event-loop, continue running the reddit browser until a 
    user quits. At each level, if the user enters 'b', go back a level. 
    Otherwise accept a number referencing a displayed item and then print 
    that item.

    Args:
        None
    
    Returns:
        None
    '''

        def collect_input():
            self.input = input('Enter Input: ')

        while True:
            if level == 0:
            elif level == 1:
            elif level == 2:

class Subreddit:
    def __init__(self, url: str, num: int, regex: str, attr: str, reverse: bool):
    '''Set up the subreddit class for use, scraping the data at instatiation time.

    Args:
        url (str): The URL for the subreddit
        num (int): The number of posts to display for the subreddit
        regex (str): The regex to use to filter posts titles
        attr (str): The attribute used to sort posts

    Returns:
        None
    '''

    def scrape(self):
    '''Scrape the subreddit

    Args:
        None

    Returns:
        None
    '''

    def display(self, loc: str, titles: bool = False):
    '''Display the titles for the subreddit along with their identifier loc

    Args:
        loc (str): The hotkey number for the user to enter the subreddit
        titles (bool): Whether the display level should be the title level or the post level

    Returns:
        None
    '''

    def filter(self):
    '''Checks if this item passes the filters it has been created with

    Args:
        None

    Returns
        bool: True or false depending on if it passes the filter checks
    '''

class Post(Subreddit):
    def __init__(self, url: str, num: int, regex: str, attr: str, reverse: bool):

    def scrape(self):
    '''Scrape the post

    Args:
        None

    Returns:
        None
    '''

    def display(self, loc: str, comments: bool = False):
    '''display the post and potentially the posts comments

    Args:
        loc (str): The hotkey number for the user to enter the post
        comments (bool): Whether to display just the posts title or the posts comments too

    Returns:
        None
    '''

    def display_comment_tree(self, reply_dict: dict, depth: int):
    '''Recursively display the comment tree within a post

    Args:
        reply_dict (dict): The dictionary of replys to the current comment
        depth (int): The depth of the current comment, used for indenting the prints

    Returns:
        None
    '''

    def filter(self, item: str):
    '''Checks if this item passes the filters it has been created with

    Args:
        item (str): The item to check the filters against

    Returns
        bool: True or false depending on if it passes the filter checks
    '''

                    
reddit_browser.py

import argparse
from reddit_classes import Browser

if __name__ == '__main__':
    # parse args

    #instantiate Browser

    #run Browser

                    

Example Output


Here are some examples of reddit.py in action:

# Show Linux subreddit
hw2 ➜ python3 reddit_browser.py --subreddits ultimate,cats,boardgames --num 3 --regex '.*'
wtheisen.github.io ➜ /usr/local/bin/python3 /Users/wtheisen/Dropbox/linux_files/programs/wtheisen.github.io/diggums/homeworks/hw2/reddit_browser.py
0 :  https://www.reddit.com/r/ultimate/hot.json
1 :  https://www.reddit.com/r/cats/hot.json
2 :  https://www.reddit.com/r/boardgames/hot.json
Enter Input: 2
2 :  https://www.reddit.com/r/boardgames/hot.json
0 : Daily Discussion and Game Recommendations Thread (February 05, 2023)
1 : Midweek Mingle - (February 02, 2023)
2 : Have you seen this mechanic in other games?
Enter Input: 2
2 : Have you seen this mechanic in other games?
|89|-> Not exactly the same, but Formula D uses different dice depending on what gear your race car is in. e.g. lower gears will use d3, d6, higher gears will use d12, d20, etc. What you roll is how far you move, but you can only shift up or down one level each turn.
  |36|-> They're special too, the D20 doesn't have like, 1-7, some of the higher numbers are duplicated.

  |5|-> If I remember correctly there could be a down side to using bigger dice though right? You HAVE to move the full amount and could cause you to crash if you go too fast. I could be wrong though it's been 20 years.


|31|-> I think this is similar to Star Wars Armada/X-Wing, where each ship rolls a different number of different colored dice (red/blue/black) for firing at different ranges (long/med/short), and each doe (all d8s) have different number of faces for each type of effect (hit/critical/accuracy/miss).  For example, a small frigate might roll 2 red and 1 blue dice whereas a Star Destroyer might roll 4 red, 2 blue, 2 black when attacking.

|7|-> I do remember a game that used different dice for different success rates. But IIRC, it had icons instead of numbers on them.

Like, the white dice hat 50% success rate (3 success icons, 3 fails), the red dice 66% (4 success, 2 fail), and the black dice 33% (2 success, 4 fail) or something like that.

Come to think of it. It might have been a TTRPG
  |4|-> Sounds like Star wars edge of the empire

  |4|-> [Conan](https://boardgamegeek.com/boardgame/160010/conan) has 3 dice of increasing strength that are used depending on the character/action. They aren't intransitive as each one is outright better than the other.   
They use yellow/orange/red for the dice, so may not be exactly what you were thinking of, but its the same principal.


|4|-> I quickly fid the math, and I believe the infantry wins only 3/16 times (not counting ties).
Out of the 36 combinations, there are 4 ties (3, 3), (4, 4), (5, 5), (6, 6). (With the first number the infantry, and 2nd the tank).
And 6 wins for the infantry (4, 3), (5, 3), (5, 4), (6, 3), (6, 4), (6, 5).
The remaining 26 combinations are all won by the tank. In matchups of multiple tanks vs multiple infantry, the advantage for the tanks is even larger.
  |3|-> Yes you are right, my mistake. For the easiest calculations, I like to write the numbers out in a table with one die on each axis like this https://imgur.com/a/aTDjRnw. I accidentally counted the infantrys winning chances as infantry-wins divided by tank-wins as opposed to divided by all non-tied battles when I made the post! I actually made a small program to simulate battles as it can get pretty complicated quickly with many units, especiallt if you have a varied army with different units fighting together. You are correct in that the advantage grows with larger battles. Two tanks vs two infantries seem to converge on a total win-rate of about 92% for the tanks for instance. However, tanks are more expensive and requires higher tech, so you'll probably have less of them. 2 infantries vs 1 tank is favoured only by 56% towards the tanks. Still tanks are most often cost-effective against infantry.


|3|-> I was 2 lines in and I already guess intransitive dice.  I have a set myself of the set designed by James Grime, where the circle of victory gets reversed when using 2 dice instead of 1.  I found that's neat.

|-8|-> That sounds pretty cool! It reminds me a bit of the combat mechanic in Scythe, with the different numbers and such
  |2|-> Thanks, haven't tried Scythe. Will look it up.
    |8|-> It is not even close to the combat mechanic in scythe.
      |3|-> Well they both have different numbers and such.


    |5|-> IIRC the combat system in Scythe is actually very different: you have a public amount of combat power and you secretly bid some amount of it using a wheel and potentially adding cards to boost it (a system originally developed in the '80s for the famous Dune game). Systems with different dice for different units or weapons are pretty common in the skirmish or dungeon crawling genre, you could check Hoplomacus or Oathsworn for recent examples (but even Twilight Imperium has a pretty similar system)
      |3|-> Twilight Imperium uses standard d10s for all rolls.




Enter Input: b
2 :  https://www.reddit.com/r/boardgames/hot.json
0 : Daily Discussion and Game Recommendations Thread (February 05, 2023)
1 : Midweek Mingle - (February 02, 2023)
2 : Have you seen this mechanic in other games?
Enter Input: b
0 :  https://www.reddit.com/r/ultimate/hot.json
1 :  https://www.reddit.com/r/cats/hot.json
2 :  https://www.reddit.com/r/boardgames/hot.json
Enter Input: 0
0 :  https://www.reddit.com/r/ultimate/hot.json
0 : Free Talk Friday: Anything goes!
1 : Florida Warm Up, Stanford Open, Mexican Mixed Nationals – Weekend Discussion [Feb 3-6]
2 : USAU Updates Observer Implementation of Misconduct System for Dangerous Plays
Enter Input: 2
2 : USAU Updates Observer Implementation of Misconduct System for Dangerous Plays
|4|-> >Moving forward in any observed game, any player that commits a dangerous play will be issued a yellow card (personal misconduct foul) or a red card (ejection) in extreme cases. **This includes instances where player initiated dangerous play calls are uncontested, even if an observer does not believe the play was dangerous.**

I like the idea behind these changes and support making the sport safer, but I worry that this specific rule change will just lead to more players contesting Dangerous Play calls. 

Big supporter of SotG, but I can't help but think we've surpassed its limit when we're effectively asking players "do you agree to be ejected?" (or "do you agree to be 1 step away from being ejected?"). 

And really, how often in an observed game does a dangerous play get missed by the observers? I feel like they almost always happen around the disc (as opposed to behind the play, or way downfield).

|3|-> Great to see this. It is overdue. The blue card system is fundamentally flawed, as the TMF is often only communicated by observers to a subset of players on a team (occasionally not even the team's leadership, which should be mandatory at a minimum) and therefore the consequences are not understood to all members of the offending team or the team's leadership may not be aware of the card at all when they are dealing with other issues during a stoppage of play.  The deterrent factor therefore can get muddied. 

Side note, why are you releasing this on Reddit and not on the USAU webpage?

|-4|-> When two players both run into unoccupied space without looking and collide with each other who is at fault? 

Because the way it is currently called and enforced dangerous plays seem to just go to whoever calls it first despite in many cases both players engaging in dangerous play

Enter Input: b
0 :  https://www.reddit.com/r/ultimate/hot.json
0 : Free Talk Friday: Anything goes!
1 : Florida Warm Up, Stanford Open, Mexican Mixed Nationals – Weekend Discussion [Feb 3-6]
2 : USAU Updates Observer Implementation of Misconduct System for Dangerous Plays
Enter Input: b
0 :  https://www.reddit.com/r/ultimate/hot.json
1 :  https://www.reddit.com/r/cats/hot.json
2 :  https://www.reddit.com/r/boardgames/hot.json
Enter Input: b
hw2 ➜                                                                                                      

We'll do it live

Note, since we are pulling data from an active website, the articles may change between runs.

Reddit API Oddities


There have been several oddities in the reddit API that have been brought to my attention. Firstly, image posts are causing lots of problems for students and secondly, every once in a while you'll get a replies dictionary that is not empty but also not formatted in the standard manner. To handle these I recommend you make use of try/except blocks. The cats subreddit especially has proven problematic and as such we will be lenient when grading in relation to this specific subreddit.

Submission Instructions


This assignment is due by 11:59 PM on Monday, February 20th (02/27). To submit, please create a folder named HW2 in your dropbox. Then put your python files, named reddit_browser.py and reddit_classes.py, into this folder. Assignments are programmatically collected at the due date.

Grading Rubric


Component Points
Browser class follows guidelines:
    - init function properly sets classes data
    - uses composition for Subreddit objects
    - run function properly collects input
    - run function allows continued browsing of subreddits
10
Subreddit class follows guidelines:
    - init gets data dict for posts on instantiation
    - uses composition for Post objects
    - display prints posts titles
10
Post class follows guidelines:
    - Inherits from Subreddit class
    - Use polymorphism for scrape function
    - Display differentiates between post title and post comments level
    - display_comment_tree implemented recursively
20
Code runs without errors using different, potentially adversarial, arguments and inputs 10
Code output correct given reasonable inputs 20
Code style 5
Total 75