0

For context, I am trying to implement a minimax algorithm in python to play tic tac toe. Here's the relevant code:

TicTacToe.py

def dumbPlayer(game, gameType):
    return game.successors()[0]


class minimax:
    tree = None
    def minimaxPlayer(self, game, gameType):
        return game.successors()[0]


class TicTacToe:
    def play(self, xPlayer=consolePlayer, oPlayer=consolePlayer, output=consolePrint):
        while self.winner is None:
            output(self)
            if self.turn == 'X': self.move(xPlayer(self, TicTacToe))
            elif self.turn == 'O': self.move(oPlayer(self, TicTacToe))
        output(self, True)
        return

main.py:

import TicTacToe as TTT

player2 = TTT.minimax.minimaxPlayer
test = TTT.TicTacToe()

test.play(TTT.dumbPlayer, player2)

Passing in the dumbPlayer function works fine, the problem comes with the minimaxPlayer function. When the play function calls the minimaxPlayer function, I get the following error:
TypeError: minimaxPlayer() missing 1 required positional argument: 'gameType' From what I understand of python, the "self" parameter is automatically passed to the function and doesn't (or maybe can't) be passed explicitly, so as far as the play function is concerned,
def dumbPlayer(game, gameType) and
def minimaxPlayer(self, game, gameType)
should be equivalent, but it appears that's not correct in this case.

Is there an easy fix for this? Or will I need to refactor my code? I already have some ideas for how I can restructure the code if I have to, but I'd rather not, and I'd like to know the underlying reason for why this doesn't work how I expect.

Frostbiyt
  • 23
  • 5
  • In your own words, where the code says `TTT.minimax.minimaxPlayer`, exactly what do you expect this to mean? In particular, **why** is `minimaxPlayer` defined within a class? That's because you *want an instance of `minimax` to exist* - the one that will be referred to as `self` within that code - in order to be able to use the code - right? So. If we just say `TTT.minimax.minimaxPlayer`, *where is the instance*? – Karl Knechtel Jun 02 '23 at 01:35

1 Answers1

1

The self argument for an instance method is automatically bound to the instance that you got the method from, but you're accessing the class's version of that method.

Instead of:

player2 = TTT.minimax.minimaxPlayer

do:

minimax_instance = TTT.minimax()
player2 = minimax_instance.minimaxPlayer

and it should behave the way you want.

Note: the standard convention in Python is to use CamelCase for class names and snake_case for instance names, which eliminates the need to use weird names like minimax_instance to differentiate between the class and the instance. You probably also want tree to be an instance attribute rather than a class attribute (if the idea is to be able to use the Minimax class with different games, you don't want the different instances stepping on each others' state)...

class Minimax:
    def __init__(self):
        self.tree = None

    def player(self, game, game_type):
        return game.successors()[0]
minimax = TTT.Minimax()
player2 = minimax.player
Samwise
  • 68,105
  • 3
  • 30
  • 44
  • That's a lot simpler solution than I expected, thanks. Not the first time I've had that sort of issue, I should've known. – Frostbiyt Jun 01 '23 at 22:02