06.0 Python‎ > ‎Python Notes‎ > ‎Python OOPs‎ > ‎

JugPuzzle: Python OOPs

Step 1: Create a well behaved Jug

When you add to a jug, make sure the jug does not spill anything.
When you remove from a jug, make sure the maximum amount removed is not more than its contents.
For both the add and remove methods, return the actual amount removed.
class Jug(object):

def __init__(self, size, currentVolme = 0):
self.__maxVolume = size
self.__currentVolume = currentVolme

def maxVolume(self):
return self.__maxVolume

def currentVolume(self):
return self.__currentVolume

def add(self, amount):
# Add at most the amount to the jug without overfilling
# amount the amount to be added
# return the amount actually added
self.__currentVolume += amount
return amount

def remove(self,amount):
# remove at most the currentVolume
# amount the amount to be removed
# return the amount actually removed
self.__currentVolume -= amount
return amount

Why self.__maxVolume and  def maxVolume(self):
What does the double underscore do to the maxVolume attribute?    

Phase 2

    def pourInto(self, otherJug):
    # remove from this jug, what was added to the otherJug from this jug's currentVolume
Create a new method to pour from one jug into the other jug.

The __str__ method

The __str__  method is a method that SHOULD be created for every class that is created.
It is a string representation an object's current state.

    def __str__(self):
Using the above def header, return a string that will include the jugs capacity and current volume.

Once this method is complete, the object can be printed by the print statement:
print (jug1)

Phase 3 The Jug Puzzle 

New Concepts:
  1. Importing from another class
    • from jug import Jug
    • jug is the file name and Jug is the class name
    • from filename import classname
    • notice that the filename starts with a lowercase letter and the classname starts with an uppercase letter
  2. Two ways of accessing the same object, two names for the same object: The concept of a pointer.
    • three jugs are created, j1, j2 and j3; these three jugs are also placed into a list
    • self.jugList = [self.j1, self.j2, self.j3]
    • now the jugs can be accessed either by the jug itself or the index in the list. ie:  j1 or juglist[0]
  3. @property decorator
    • creating implicit attributes of an object
    • example:  isGameOver
    • this is example of a "getter" property, later you will be introduced the "setter" property
  4. The dot operator
    • calling a method from within the object
Copy and paste this code, modify the code as instructed in the comments
Add the logic to keep track of the number of moves made.

from jug import Jug
class JugPuzzle():
def __init__(self):
self.j1 = Jug(8, 8)
# make an empty jug of size 5
# make an empty jug of size 3
self.jugList = [self.j1, self.j2, self.j3]
# make a attribute to keep track of the numberOfMoves and set it to zero

def __str__(self):
s = "Jug 1: " + str(self.j1) + "\nJug 2: " + str(self.j2) + "\nJug 3: " + str(self.j3) \
+ "\nNumber of Moves: " + str(self.numberOfMoves) \
+ "\nIs the Game Over: " + str(self.isGameOver) + "\n"
return s

def isGameOver(self):
return self.j1.currentVolume() == self.j2.currentVolume() == 4

def pour(self, fromJug, toJug):

def nextMove(self):
jugIndex = "123"
jFrom = jugIndex.find(input("from Jug: "))
jTo = jugIndex.find(input(" to Jug: "))
self.pour(self.jugList[jFrom], self.jugList[jTo])

Some reading about properties:  http://www.python-course.eu/python3_properties.php
Look for these words and understand their use:
  • interface
  • encapsulation
  • getters, setters /mutators

Play Jug Puzzle

Create a new class to play the jug puzzle.
Logic of PlayJugPuzzle:

create a new JugPuzzle
display the JugPuzzle
While the jug puzzle game is not over
    get the jug puzzle's next move
    display the JugPuzzle