Saturday, January 21, 2017

Gaze at my syllogisms!

So I have been hacking away at the Text Adventure (pun intended) and I implemented the wear and wield actions yesterday.  They were far more complicated than I gave them credit for.  The character needs to wear and wield armor, but then I thought about the problem more, and it turned out that actually:

Characters have body slots to wear and wield weapons
Only weapons go in the Main Hand and Off Hand slots and only armor can be worn.
Characters have class constraints as to what they can wear and wield
Some weapons are two handed, some are not
Some characters can dual wield, others cannot
Main hand is always equipped first
Needs to support the use of keywords

I remembered that it is best to unit test first, then build your code.  I literally went through all of my axioms for the system, and then test by test built up the function to make sure it was working properly.  I LOVE pytest fixtures, because they made the testing process a whole hell of a lot easier.  Testing helps you to narrow down what the function is actually supposed to do.  I tried to test vigorously, going for each permutation and thinking about how to narrow down the possibilities so that the player is only allowed possibilities I want.  Yes, there was probably a less verbose, better way of doing all this, but it works and all of my unit tests passed.  Behold, my wield function in all of its syllogistic glory!

class WieldAction():
    def __init__(self,item_repository):
        self.item_repository = item_repository

def do(self,state,itemname):
    player = state.player
    if not itemname:
        print("Wield what?")
        return    for itemid in player.inventory:
        item = self.item_repository.get_by_id(itemid)
        if itemname == item.get_name():
            #Is it a weapon            if item.get_type() != "Weapon":
                print("That cannot be wielded.")
                return            #Does your class allow you to wield this?            elif item.get_weapon_type() not in player.guild.get_allowed_weapon_types():
                print("{0}s are not allowed to wield {1}s".format(player.guild.get_name(), item.get_weapon_type()))
                return            #Druids can't wield metal objects            elif player.guild.get_name() == "Druid" and item.get_material() in ["iron", "steel", "copper", "bronze"]:
                print("Druids may not wield weapons made of metal.")
                return            elif item.get_handed() == 2:
                if player.race.get_body_slot("Main Hand")==0 and player.race.get_body_slot("Off Hand") == 0:
                    player.race.set_body_slot("Main Hand", item.get_id())
                    player.race.set_body_slot("Off Hand", item.get_id())
                    print("You wield the {0} in both hands.".format(item.get_name()))
                    player.inventory.remove(item.get_id())
                    return                else:
                    print("You need two free hands to wield the {0}.".format(item.get_name()))
                    return            elif player.race.get_body_slot("Main Hand") != 0:
                if "Two Weapon Fighting" not in player.get_feats():
                    print("You cannot dual wield.")
                    return                elif player.race.get_body_slot("Off Hand") != 0:
                    offhanditem = self.item_repository.get_by_id(player.race.get_body_slot("Off Hand"))
                    print("You are already wielding {0} in your off hand.".format(offhanditem.get_name()))
                    return                else:
                    player.race.set_body_slot("Off Hand", item.get_id())
                    print("You wield the {0} in your off hand.".format(item.get_name()))
                    player.inventory.remove(item.get_id())
            else:
                player.race.set_body_slot("Main Hand", item.get_id())
                print("You wield the {0} in your main hand.".format(item.get_name()))
                player.inventory.remove(item.get_id())
        if itemname in item.get_keywords():
            #Is it a weapon            if item.get_type() != "Weapon":
                print("That cannot be wielded.")
                return            #Does your class allow you to wield this?            elif item.get_weapon_type() not in player.guild.get_allowed_weapon_types():
                print("{0}s are not allowed to wield {1}s".format(player.guild.get_name(), item.get_weapon_type()))
                return            #Druids can't wield metal objects            elif player.guild.get_name() == "Druid" and item.get_material() in ["iron", "steel", "copper", "bronze"]:
                print("Druids may not wield weapons made of metal.")
                return            elif item.get_handed() == 2:
                if player.race.get_body_slot("Main Hand")==0 and player.race.get_body_slot("Off Hand") == 0:
                    player.race.set_body_slot("Main Hand", item.get_id())
                    player.race.set_body_slot("Off Hand", item.get_id())
                    print("You wield the {0} in both hands.".format(item.get_name()))
                    player.inventory.remove(item.get_id())
                    return                else:
                    print("You need two free hands to wield the {0}.".format(item.get_name()))
                    return            elif player.race.get_body_slot("Main Hand") != 0:
                if "Two Weapon Fighting" not in player.get_feats():
                    print("You cannot dual wield.")
                    return                elif player.race.get_body_slot("Off Hand") != 0:
                    offhanditem = self.item_repository.get_by_id(player.race.get_body_slot("Off Hand"))
                    print("You are already wielding {0} in your off hand.".format(offhanditem.get_name()))
                    return                else:
                    player.race.set_body_slot("Off Hand", item.get_id())
                    print("You wield the {0} in your off hand.".format(item.get_name()))
                    player.inventory.remove(item.get_id())
            else:
                player.race.set_body_slot("Main Hand", item.get_id())
                print("You wield the {0} in your main hand.".format(item.get_name()))
                player.inventory.remove(item.get_id())

Saturday, January 14, 2017

Fighting the Burnout

It can be incredibly easy to burn out.  So far, I've been learning programming since the beginning of the year, going on 2 or 3 weeks, and I'm still going strong.  After a six hour stint in the library though, I start to run out of gas.  I get home, put on Netflix, and my brain feels like mush.  I never realized that programming had such a high cognitive load and took so much concentration and mental energy to do.  I'm loving it so far!  Let's get to what I've been learning.

Today I started learning SQL, which stands for "Structured Query Language."  It's a language used for handling databases, grabbing information, changing them, etc.  I asked some of my (apparently) many programming friends if it was useful.  The answers I got were:

"Meh?"

"I use it at my job sometimes.  I mostly Google what I need."

"If I didn't use it, a meteor might destroy myself and everyone at work.  LEARN IT NOW!"

"The sequel to most movies is usually terrible."

So, given the rave reviews, I decided to start learning it so that I wouldn't be ignorant of something that later on down the line I might find useful.  I'm one of those people where I think today was a good day if I go to bed slightly less ignorant than when I woke up.  So, I fired up Code Academy and started doing SQL.  When I think databases, I think of vast expanses of impenetrable numbers, like the matrix.

Instead, I found out SQL is extremely accessible and easy to understand.  I started doing some of the Code Academy problems, and at one point, it asked me to get all the movies who's IMDB rating is 8 or above.  Without even looking at the helper screen (Code Academy tends to hold your hand), I just typed in:

SELECT id FROM movies
WHERE imdb_rating >=8;

What shocked me was this was CORRECT. I started just laughing at my keyboard.  Without any coaching you can just GUESS what the syntax is!  My goal is to finish Code Academy tonight and then move on to more complicated stuff so I can feel like I have a handle with the language.


Tuesday, January 10, 2017

The Factory and Repository Pattern

So I bought a copy of "Learn Python the Hard Way" which I highly recommend to any would-be programmer learning Python.  The mid level project is to work on your own text adventure game.  It's supposed to be "Zork" like, with set rooms that you navigate through until you reach the end of the story.  The project is supposed to use all knowledge taught thus far, including "while" loops and "for" loops, as well as object inheritance and composition.

I took things a bit further.

I have been studying design patterns.  They're kind of like joseki in the game of Go, set patterns that are solutions to different situations.  After all, in software engineering, you come across the same problem multiple times, or at the very least, similar problems with similar solutions.  I've been reading "Head First Design Patterns," scouring the internet, and getting help from a software engineer friend of mine, and found two patterns that I found useful: The Repository and Factory pattern.

Here's my problem: the game is a text adventure.  You walk around rooms, talk to NPCs, fight monsters, pick up items, and fight.  During my first go through, my code was starting to get to be complicated.  I decided I wanted to save my items, npcs, rooms, etc. as JSON files.  That way, I can simply drop the rooms into a folder called "rooms" and just have the computer load the file, read the information, and create a new Room object or Weapon object or Silly String object.  Whatever I needed.  The game would be filled with different types of objects I would need on demand.

Here's where the patterns come in.

The Repository pattern is a way to request a certain kind of object from a "repository" of objects  you might need.  The factory is a way of constructing different objects you need on demand.  I linked it to a File loader object.  Essentially, the repository receives a request for a specific kind of object, then the file loader loads the JSON file, wherever it might be.  The factory creates the right kind of object (be it a room, a weapon, or a NPC) and returns the object to the repository.


What's great is that each part isn't "coupled" tightly with each other.  The repository just needs to know which factory to load and which file loader, past that it has no idea how a file is actually loaded, and definitely not HOW to make the object in question.  The file loader just takes an ID and a file path already given to it, and spits out a dictionary of information.  Does it have any idea the difference between Gandalf the Gray and a stick of butter?  Nope!  And it doesn't need to.  The factory basically just looks at the "type" part of the dictionary, and puts it through the sausage machine.  This makes it easy to use the factory for all kinds of things, including spells, monsters, and even actions.  

The main thing I learned is you need to de-couple one part of a program from another as best you can.  I don't want the repository to have deep tendrils into the factory, or else, if I have to change something about the factory, then the repository might also need to be re-coded.  The more complexity, the more chances there will be bugs.  

The next part of the game is to create a view for the player.  I was thinking a Game Screen class that spits out information at the player whenever the "Game State" changes.  So if the id changes, then it can have functions like "Print Room" etc.  That way, the action classes don't need to really care about what is being displayed to the player, they just need to conduct their own checks.  

I smell Observer pattern...but we'll get to that next time.

Monday, January 9, 2017

New Blog, New Posts, New Life Path

I decided to start a blog about my coding (mis)-adventures and share my journey from a layman to a software engineer.  I decided I wanted to take the next six months of my life and train myself to become a software engineer.  My previous profession was teacher, which I had been doing dutifully for seven years.  Teaching was great. It got me out of the country and into China and South Korea, so I feel like I've taken the time to explore some of the world.  I've taught pretty much every subject imaginable.  Math, Science, History, Chinese, Philosophy, if it's a subject, I've taught it.  I felt I had hit a plateau in teaching and there were few frontiers to expand.  

So then, why software engineering?  Why computers?  Why coding?  Easy!  It's a growing field, companies need tech people like there's no tomorrow, so I'll have an easier time becoming employed, There's plenty of room for me to make a meaningful contribution to the field.  My main goal in life is to do something that I'll be remembered for!

My coding journey starts at 14.  I used to make websites for my mom and her business associates.  I taught myself HTML and CSS.  I was a huge text MUD nerd.  I played a MUD called "Lost Souls" for hours.  Eventually, I decided to start my own MUD, "Wanderlust Realms."  I never got it live, but I learned a TON while trying to do so.  I began teaching myself C, back when it was in vogue, but then, I just left programming alone.  I didn't see much point in doing it after I became disinterested in MUDs, and then I went on to teaching. 

In some small way I wish I had stuck with it, had pushed further.  In the 12 years it has been since I stopped, I have learned all about languages and platforms I had never seen or heard of.  It's all been rather exciting, getting back into the swing of learning.  

The first language I am tackling is Python.  I have the concepts of while and for loops down, functions, and closures.  I already understood the concepts of inheritance and composition, and Object Oriented programming has always been something that has made intuitive sense.  

This blog will chronicle my day to day.  I am studying like it's my job...literally.  I will try to make posts explaining my pitfalls and "Eureka!" moments, and my readers are encouraged to chime in, especially my readers with programming experience!