4
\$\begingroup\$

Script simulates the Martingale betting strategy of betting a fixed amount until a loss occurs, at which point the bettor doubles the bet to make up for the loss. This continues until a win occurs. After a win, the bet is reset to the original bet value. I set the odds to mimic Blackjack (49% chance of a win). For simplicity, the amount won or lost in a round is equal to the bet. The simulation ends when the specified number of rounds has elapsed, the size of the next bet is larger than the current available funds, the available funds reach 0, or the goal profit is met.

I'm brand new to Python and coding in general, so any feedback would be appreciated.

import random


class MartGame:
    def __init__(self, bet=25, starting_funds=5000, goal_profit=1000, round_n=0, rng_v=0.00,
                 wins=0, losses=0, loss_run=0, win_run=0):
        self.bet = bet
        self.starting_funds = starting_funds
        self.goal_profit = goal_profit
        self.round_n = round_n
        self.rng_v = rng_v
        self.wins = wins
        self.losses = losses
        self.los_run = loss_run
        self.win_run = win_run
        self.original_bet = bet
        self.current_funds = self.starting_funds

    def rng(self):
        """Generates random number"""
        self.rng_v = random.random()

    def winloss_generator(self):
        """Generates win/loss condition"""
        if self.rng_v <= .49:
            return 'win'
        else:
            return 'loss'

    def increase_winloss(self):
        """Increases wins or losses variable based on winloss_generator output"""
        if self.winloss_generator() == 'win':
            self.wins += 1
            return 'win'
        elif self.winloss_generator() == 'loss':
            self.losses += 1
            return 'loss'
        else:
            print('error')

    def increase_round(self):
        """Increases round number by 1"""
        self.round_n += 1

    def change_current_funds(self):
        """Increases or decreases current_funds based on winloss_generator output"""
        if self.winloss_generator() == 'win':
            self.current_funds += self.bet
        elif self.winloss_generator() == 'loss':
            self.current_funds -= self.bet
        else:
            print('error')

    def change_bet(self):
        """If outcome is a win, bet is reset to original value. If outcome is a loss bet is doubled"""
        if self.winloss_generator() == 'win':
            self.bet = self.original_bet
        elif self.winloss_generator() == 'loss':
            self.bet = self.bet * 2

    def running_profit(self):
        """Returns running profit"""
        return self.current_funds - self.starting_funds

    def current_funds(self):
        """Returns running total of funds"""
        return self.current_funds

    def print_current_record(self):
        """Prints current win/loss record to screen"""
        print('Current Record: ', self.wins, 'wins, ', self.losses, 'losses')

    def print_winloss(self):
        """Prints win/loss condition to screen"""
        print(self.winloss_generator())

    def print_profit(self):
        """Prints running profit to screen"""
        print(self.running_profit(), 'running_profit'.upper())

    def print_running_total(self):
        """Prints running total to the screen"""
        print(self.current_funds, 'current funds'.upper())

    def print_round(self):
        """Prints current round"""
        print('Round:', self.round_n)

    def print_current_bet(self):
        """Prints current bet"""
        print('Bet:', self.bet)

    def run(self, rounds, each=False):
        """Runs simulation for specified number of rounds. 'each' argument indicates
        whether each round should be displayed. False will only display the final results"""
        while self.running_profit() < self.goal_profit and \
                self.round_n < rounds and self.current_funds > 0 \
                and self.current_funds > self.bet:
            self.increase_round()
            self.rng()
            self.winloss_generator()
            self.increase_winloss()
            self.change_current_funds()
            if each is True:
                self.print_round()
                self.print_current_bet()
                self.print_winloss()
                self.print_running_total()
                self.print_profit()
                self.print_current_record()
                print()
            self.change_bet()
        print()
        self.end_script()

    def end_script(self):
        """Prints final outcome of the simulation and summary of the results"""
        if self.running_profit() == self.goal_profit:
            print('YOU WIN!')
        else:
            print('YOU LOSE')
        print("Total Rounds:", self.round_n)
        print('Win/Loss Record: ', self.wins, 'Wins', self.losses, 'Losses')
        print('Ending Funds:', self.current_funds)
        print('Goal Profit:', self.goal_profit)
        print("Ending Profit:", self.running_profit())


inst = MartGame(25, 5000, 1000)

inst.run(1000, True)
```
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

Classes are useful when you want to create more than one object from the same class, or create and destroy objects while running. In this case, you just have one object.

This is not the kind of program where you need a class. If you didn't use a class, you could have the exact same program but remove every self. in your code, which makes it shorter and easier to read and write.

All of this is not needed either (when you don't use a class) since variables will just be global within the script, or local within your main() function if you have one.

    self.bet = bet
    self.starting_funds = starting_funds
    self.goal_profit = goal_profit
    self.round_n = round_n
    self.rng_v = rng_v
    self.wins = wins
    self.losses = losses
    self.los_run = loss_run
    self.win_run = win_run
    self.original_bet = bet
    self.current_funds = self.starting_funds

The function below is also meaningless, whether you use a class or not. It is only used in one place in the code, so it would save lines of code and brainpower for the developer to just increment the variable directly in that one place, not do it via a function.

def increase_round(self):
    """Increases round number by 1"""
    self.round_n += 1

The function below is even more useless. It is not even called since you are (correctly) referring the variable directly where used. It also has the same name as the variable current_funds , which might lead to problems.

def current_funds(self):
    """Returns running total of funds"""
    return self.current_funds

The below looks weird. Why don't you just print 'RUNNING_PROFIT' ?

'running_profit'.upper()
\$\endgroup\$
7
  • \$\begingroup\$ Thank you for the feedback. I originally wrote this script without a class and was told by a user on Stack Overflow that I should implement one. I was using the globals everywhere in almost every function to keep track of running totals which would otherwise be reset by the function every time it ran. What would be a good solution for that problem? \$\endgroup\$ Commented Sep 15, 2020 at 23:45
  • \$\begingroup\$ I don't understand the question, and I don't see the problem. Perhaps it would help to see the version you wrote without a class. In the current program, the class is only initialised once, all the variables are "effectively global" within the object itself, and all the code runs within that single object. So encapsulating all of this within an object does nothing really. It just adds a lot of code. \$\endgroup\$
    – user985366
    Commented Sep 16, 2020 at 0:20
  • \$\begingroup\$ Found your original post. You defined running_profit = running_total - starting_funds and answers/comments explained why that didn't work. It does not create a dependency, and the solution is like you did in this code, to create a function for it instead so that it is calculated each time you need it. \$\endgroup\$
    – user985366
    Commented Sep 16, 2020 at 0:28
  • \$\begingroup\$ Also, from the suggestions to the StackOverflow post, I agree that it would be good to create a main function and keep the variables ("state") inside, while using other functions to calculate intermediate values as needed. Values will be passed between functions, but you shouldn't need globals. \$\endgroup\$
    – user985366
    Commented Sep 16, 2020 at 0:30
  • \$\begingroup\$ The problem with the program you posted on SO is that you're not passing parameters into your functions, and in some cases not returning values either. If you did, you wouldn't need any globals. If you post that program here on codereview, I'll suggest some changes. \$\endgroup\$
    – user985366
    Commented Sep 16, 2020 at 0:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.