Mini Cactpot Algorithm 1

This is the first part of the mini cactpot algorithm. If you want to take a look at the complete code, click on the link: FF14 Mini Cactpot Calculator. I will be talking about two main classes of the program, Game and Calculate. The class Game deals with displaying the ticket to the user, getting user inputs, and updating the ticket accordingly. The class Calculate does the heavy lifting by calculating all possible combinations of three number lines on the ticket and the payout of each.

Class Game

Since the ticket is a 3×3 matrix, I first thought of using a nested list for internal representation of the ticket. I could use a list comprehension for this and use 'x' as default values of each value.

ticket = [['x' for j in range(3)] for i in range(3)]

>>> ticket = [['x', 'x', 'x'],
>>>           ['x', 'x', 'x'],
>>>           ['x', 'x', 'x']]

However, I realized when I tried to update user inputs to the nested list is that it can become complicated. I would need to keep track of each position inside the nested list based on its index value. So the first position is ticket[0][0] and the last position is ticket[2][2]. Then the user will need to enter their own numbers, which can lead to very messy code and prone to error. I decided against using nested list and just use a dictionary to map letters of the alphabet to each position.

ticket = {'a': 'a', 'b': 'b', 'c': 'c', 'd': 'd', 'e': 'e', 
          'f': 'f', 'g': 'g', 'h': 'h', 'i': 'i'}

This helps me out significantly. Now I can pinpoint exactly where each position is based on their letter key. Then I use a class for ANSI escape sequences for text decoration to makes strings stands out in the console. This is optional but it helps with usability.

class Color:
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'
    END = '\033[0m'

With the appropriate data structure set, now I can start writing out the class Game that takes one argument, the dictionary ticket.

class Game:
    def __init__(self, ticket):
        self.ticket = ticket

The user will be interacting with the program so a display method is needed. In my case I used a list for each line of string. On lines where the ticket numbers are, string formatting is useful. Just join them all together with \n in the return statement.

def new_ticket(self):
    display = [
        Color.BOLD + '4  5  6  7  8' + Color.END,
        Color.BOLD + '3' + Color.END + '  {}  {}  {}'
        Color.BOLD + '2' + Color.END + '  {}  {}  {}'
        Color.BOLD + '1' + Color.END + '  {}  {}  {}'
    return '\n'.join(display)

>>> 4  5  6  7  8
>>> 3  a  b  c
>>> 2  d  e  f
>>> 1  g  h  i

The user can easily see where each 3-number lines are on the ticket by number 1-8 surrounding the ticket. The next step is to get the four numbers chosen by the user. The user simply enters the letter of the ticket and the number chosen. I use a for loop to get each letter-number pair, split the inputs and store them in user_inputs dictionary. This is static method that doesn’t use the class Game object at all.

def user_numbers():
    user_inputs = dict()
    for counter in range(1, 5):
        letter, number = input('Number {}: '.format(counter)).split()
        user_inputs[letter] = int(number)
    return user_inputs

The next method is fill_numbers. It takes the values from the dictionary returned in user_numbers and fill them in the class attribute ticket if their dictionary keys match each other.

def fill_numbers(self, numbers):
    Fill in numbers to ticket from dictionary of user's inputs
    for key, value in numbers.items():
        for letter in self.ticket:
            if letter == key:
                self.ticket[letter] = value
    return self.ticket

Those are all the initial setups for the program. In the next post I will be talking about the actual calculation of all potential combinations in the class Game. Click here to go to next part: Part 2.

Leave a Reply

Your email address will not be published. Required fields are marked *