Sunday, January 31, 2016

w3d5 - 2 days late with a reality check on OOP

Well, that was brutal.

I left off Thursday evening with grand plans for a general card game constructor. I limped in to Sunday night with a functional blackjack game built with python classes.  The basic structures I outlined survived.  I learned some good lessons in debugging long scripts, step-wise validation of functional code, and yet another lesson on data structures.

On data structures and class variables: be cautious.  Using

class Player(object):
      def __init__(self, name='', data=[]):
             self.name = name
             self.data = data

looks to the naive as though it will save some key strokes when initializing instances .  As it turns out, each instance of Player has different name, but their self.data points to the same list.  E.g.:

>>>bob = Player(name='bob')
>>>jeff = Player(name='jeff')
>>>
>>>bob.data.append('yo')
>>>
>>>jeff.name
>>>'jeff'
>>>jeff.data
>>>['yo']

It recalls the points raised in this Stack Overflow thread on how to be a good programmer (specifically wscpy's answer highlighting the difference between how python stores integers or strings and how it stores(references) lists).

I have so much to learn about the stuff under the hood.

On debugging:

I have more lines devoted to test prints for individual functions (to monitor what various pieces of the script are doing) that I had in any program I wrote before I got to camp.  Still small relative to 'real' stuff, but still...

And also - even when you think you know what you're doing, test shit.  I might get that as a tattoo and save myself a ton of future pain.


Thursday, January 28, 2016

w3d4 - Making a blackjack game with OOP (classes in python)

Our week 3 capstone project is to build a blackjack game using classes.  After half a day, I'm about half way done. It would be faster, but I spent a lot of time (at the instructors urging) planning my approach.  Since I'd gotten pretty good with data structures in previous weeks, I naturally thought of doing the project with lists and dictionaries with a bit of class to hold the pieces together, so to speak.  Thinking in OOP is not a huge stretch, but it's not as natural...which is the point of this project, I suspect.

My goal is build not just a blackjack game, but any game that can be played with normal deck of cards (or multiple decks...).  It should accomodate Jokers and games other than black jack with a few modifications to the variables used to construct the deck, and with a unique Game.play().

The basic outline of my game is:

class Card
   properties: name(number, or face name, or Ace), suit, value, specialflag (for cards with special properties like the Ace), symbol (in case I ever get around to figuring out how to print the suit symbols)
   method: __repr__ override to print card.name + card.suit when "print card" is used

class CardGroup
   properties: container (to hold cards)
   methods: print cards (relies on card __repr__ override

class Deck(CardGroup) (Deck is a subclass of CardGroup)
   properties: suits (used in constructing a deck - any set of suits could theoretically be used)
                     name, value tuples - connects the card name with it's value.  All face cards in blackjack are worth 10, but hearts would be different, and in poker only name and suit would matter.
                      container (Deck and Hand are subclasses of CardGroup so their self.container needs to point to different things)

   methods: make deck (based on suit, value, name info), shuffle deck (just random.shuffle), draw_card to pop a chard off the end of the list of cards

class Hand(CardGroup)
   properties: container
  methods: add_card (takes the draw_card result from the Deck), play card (not yet implemented since blackjack doesn't use that method), evaluate hand (to decide the status of the hand - just adds the point value at present which undercuts universality for other kinds of games)

class Player
  properties - name, hand, money
  methods - draw starting hand, take turn (still working on how this and Game.play() interact, but should use polymorphism

class Dealer(Player)
  properties as above
  methods - as above, but 'take turn' will be a simple AI to replicate the rules of blackjack

class Game
  properties - players, deck
  methods - add player, add deck, take turn, get new deck, monitor progress/declare winner


So, yeah.  It's a bit of overkill.  It's fun to see how the classes interact, though.  It's also a hell of a confidence boost to see this coming together after the fiasco of trying to implement linked lists using objects.  More on that later...
 


Wednesday, January 27, 2016

w3d3 - Linked Lists and binary trees...now with classes!

I have to confess that today was...intense.  It's painfully clear that I know much less about how classes work than I thought.  It was honestly a good lesson in humility for me, and it was a reminder of how much work I have to do.

 Everything was working by the end of the day, even a little extension project on recursive pathfinding through a graph:

graph = {'A': ['B', 'C'],
         'B': ['C', 'D'],
         'C': ['D'],
         'D': ['C'],
         'E': ['F'],
         'F': ['A','C']}

where each key is linked by a one-way path to it's  corresponding nodes, which are in turn linked to other nodes.  Given a start and an end, can you find a path between.  I can get it to work for ONE path, and screen out loops (see that tricksome 'C' --> 'D' --> 'C'-->...) but not to collect ALL paths, or to find the shortest path.

The first task of the day was creating and modifying linked lists.  For linked lists, I could make a "Node" class with  'data' and  'next node' properties.  Using functions,  I could print out the nodes in order, add new data to the tail of the node, return data from a node, and insert a node between two others.  Do it classes, either within the Node class or in a separate "Linked List" class, though?  Nope.  Or, I should say, 'not yet'.  I will get this because I need to know classes better, to really understand how data is held.  I've seen some really elegant solutions online, and I'm debating continuing to try to write my own, or to look at a professional-grade one and try to reverse engineer it.

After linked lists came binary trees.  The concept is fascinating.  I love that a thing that looks so disordered at first glance holds such structured data.  I could get a basic recursive tree-reader function working after a lot of pain, dead ends...and mis-read directions.  The solution to printing out the data in a binary tree in order (least-to-greatest) using a recursive algorithm was painfully simple once discovered.  Getting there?  Very challenging.

I will say that I really love the concept of recursion. There's something really satisfying about defining a data-processing approach that takes in bites from a big pile until it's done.  Or better, than spawns copies of itself to check out branching paths and report back.

Working with classes + recursion ==> painful.  These are hard for me in part because it's a lot more laborious to prototype in the REPL using classes.  Simple functions I can do there easily, or to test out methods on default classes. (huh.  Maybe if I work on treating my created classes like the built in classes (string, list, hash, etc) I'm more comfortable with, the syntax will make more sense.).

Tomorrow is another day.  I'll set an alarm to wake up early and try the classes with linked lists project one more time.

Tuesday, January 26, 2016

W3D2 - Inheritance and Polymorphism, and pair programming

The interesting thing about pair programming is that even when partners are very mismatched, having to justify and explain can help the more skilled programmer really think through design decisions.  For the method to work well, the less skilled programmer has to be proactive about asking questions.  If they just sit back and lets the more skilled do all the work, they learn nothing from the exercise.

On the ostensible subject of the lesson - there were a few interesting new concepts.  One would be iterating through an array of instances of classes and calling an identically-named method for each.

As far as inheritance and polymorphism, though, the exercises made sense.  I'm sure there's a lot more to learn, but so far so good.

Week 3, day 1 - OOP and making my own hash

Part 2: OOP

The best part of today was learning how hash tables (aka dictionaries) work on a fairly basic level.  Essentially, the key of a dictionary entry {'key': 'value'} is converted to a number using a hashing function.  The essential elements of a hashing function are:  a prime number and a means of converting a text string to a number.  The prime can be of arbitrary size, but any reasonably sized dictionary would need a fairly large number.  The unicode values for letters in the key can be used as one way of generating a number.  When that number is divided by the prime, a semi-unique value is determined. (semi-unique because there will inevitably be a collision when two keys generate the same index).

One of the other interesting things about this project was building the hash as a class.  Doing so required overloading the __getitem__ and __setitem__ built-in methods.  I'd seen classes a bit in Learn Python the Hard Way and Code Academy, but I had no idea that built-in overload was possible.  So that was two lessons in one...

As a further extension, and third part of the lesson, I built a collision-handling mechanism whereby, instead of simply appending a value to the index, a key:value tuple was appended.  Then, when my class __getitem__ was called, the lookup method would iterate through the tuples looking for the matching key.


Monday, January 25, 2016

Week3, day 1 - recursive file indexer wrap up

First - I was able to wrap up the week two project:  a text file search script that assembles a list of text files in a given arbitrary directory tree using a recursive search, then indexes the words in the files such that a user can discover which files contain the word of interest.

I managed to complete my own objectives, and added in a list of words to ignore (e.g. 'the', 'a', pronouns, etc.).  Courtesy of stackoverflow, I was able to add in a function to open files using the default program for each file.  Since I'm working with only .txt and .csv, that's not a huge range of options.  The script looks for os before opening, since mac and windows use different commands.  Net result: I learned a bit about subprocess module, and more about the os module.

I also was able to re-use my entry validation module to ensure users were selecting valid options.

My stretch goal: index large files (e.g. whole books as text files, courtesy of the Gutenberg Project.  My goal is to collect the line number where a word appears in a file to make words reasonably findable.  My script should also have a function to provide a preview of the context of a word.


Friday, January 22, 2016

Week 2 is a wrap - a mini google

The 'capstone' project for week two is to create a miniature version of an indexed search engine for text files.  The basic version should:

1.  scan through a file structure and make a list of files and associated path names.
2.  read all the text files to create an array of words
3. create a dictionary of {'word1': {file1: path, file2: path}, 'word2: {}}

The list of files is produced using the recursive tool built earlier in the week.

At this point, I have a working version of the project that accepts only files with '.txt' in the name.

To do, in rough order of importance:

1. exception handling for words not in the dictionary that a user tries to look up.
2. remove punctuation and formatting from text before reading for the library.
3. validation on user input (did they type a word, or put in random characters)
4. ask user if they wish to open one of the files in  a text editor
5.  open and index other file types (e.g. csv)

All of that and general edits for readability and cleanliness.

I might be further along by now, but I took some time this morning (it was an unstructured work day) to refactor my budget app file-open function.  After the refactor, it now appends the data from an opened csv to the in-script dictionary (and gets the keys right).  Opening a csv file creates a dictionary with key names = column headers in the csv.

Also, I built a script to make temporary files in my practice file tree.  Each file is filled with a random number of words organized into lines.  Since the function uses the recursive file tree generated above, pseudo-text-files are scattered thoughout the sample directory tree.

And now it's after midnight on a day that started with the dawn.  More later.