Code editing task: strategy
Topic: DSA; change kind: adaptive

Original code:
from abc import ABC
from abc import abstractmethod
from typing import List, Tuple

class Strategy(ABC):
    @abstractmethod
    def returnMove(self, board: List[List[bool]]) -> Tuple[int, int]:
        '''Returns a tuple(row, column) which indicates where to move 
           in a 3x3 grid.'''
        pass

class CornerStrategy(Strategy):
    def returnMove(self, board: List[List[bool]]) -> Tuple[int, int]:
        if board[0][0] == None:
            return (0, 0)
        elif board[0][2] == None:
            return (0, 2)
        elif board[2][0] == None:
            return (2, 0)
        elif board[2][2] == None:
            return (2, 2)
        else:
            raise Exception
        
class Game:
    def __init__(self, player1: Strategy, player2: Strategy):
        self.playerOne = player1
        self.playerTwo = player2
        self.board = [[None for _ in range(3)] for _ in range(3)]

    def player1Won(self):
        playerTurn = True
        while not self.playerXWon(True) and not self.playerXWon(False) and not self.gameOver():
             strat = self.playerOne if playerTurn else self.playerTwo
             move = strat.returnMove(self.board)
             self.board[move[0]][move[1]] = playerTurn
             playerTurn = not playerTurn
        if self.gameOver():
            return False
        else:
            return self.playerXWon(True)


    def gameOver(self):
        for row in self.board:
            for col in row:
                if col == None:
                    return False
        return True
    
    def playerXWon(self, x: bool):
        for i in range(3):
            if self.rowNX(i, x):
                return True
        for i in range(3):
            if self.colNX(i, x):
                return True
        downDiag = self.board[0][0] == x and self.board[1][1] == x and self.board[2][2] == x
        upDiag = self.board[2][0] == x and self.board[1][1] == x and self.board[0][2] == x
        return downDiag or upDiag
    


    def rowNX(self, n: int, x: bool):
        for col in self.board[n]:
            if col != x:
                return False
        return True
    def colNX(self, n: int, x: bool):
        for row in self.board:
            if row[n] != x:
                return False
        return True

Instruction (descriptive):
Create a class `GoodStrategy` which extends `Strategy` such that `Game(GoodStrategy(), CornerStrategy()).player1Won()` returns `True`.
This can not be solved by modifying the `Game`, `Strategy`, or `CornerStrategy` classes in any way.
The following code describes a tic-tac-toe game which takes in two strategies and determines who wins if they play each other. The `Strategy` class defines an abstract method, `returnMove(board)`, which returns a tuple representing where this strategy will move, given a board state.
The `CornerStrategy` class is a subclass of `Strategy` with a concrete implementation of `returnMove(board)`. The `Game` class constructor takes in two strategies. It has a method `player1Won` which determines if the first strategy provided will beat the other if they both take turns alternating between moves.
There are two methods, `playerXWon` and `gameOver` which determine how a game is won and when it is over.

Instruction (lazy):
Create a strategy `GoodStrategy`, that beats `CornerStrategy`. Do not modify the `Game` class.

Edited code:
from abc import ABC
from abc import abstractmethod
from typing import List, Tuple

class Strategy(ABC):
    @abstractmethod
    def returnMove(self, board: List[List[bool]]) -> Tuple[int, int]:
        '''Returns a tuple(row, column) which indicates where to move 
           in a 3x3 grid.'''
        pass

class CornerStrategy(Strategy):
    def returnMove(self, board: List[List[bool]]) -> Tuple[int, int]:
        if board[0][0] == None:
            return (0, 0)
        elif board[0][2] == None:
            return (0, 2)
        elif board[2][0] == None:
            return (2, 0)
        elif board[2][2] == None:
            return (2, 2)
        else:
            raise Exception
        
class GoodStrategy(Strategy):
    def __init__(self) -> None:
        super().__init__()
        self.turn = 0

    def returnMove(self, board: List[List[bool]]) -> Tuple[int, int]:
        self.turn += 1
        if self.turn == 1:
            return (0, 1)
        elif self.turn == 2:
            return (1, 1)
        elif self.turn == 3:
            return (2, 1)
        raise Exception
   
        
class Game:
    def __init__(self, player1: Strategy, player2: Strategy):
        self.playerOne = player1
        self.playerTwo = player2
        self.board = [[None for _ in range(3)] for _ in range(3)]

    def player1Won(self):
        playerTurn = True
        while not self.playerXWon(True) and not self.playerXWon(False) and not self.gameOver():
             strat = self.playerOne if playerTurn else self.playerTwo
             move = strat.returnMove(self.board)
             self.board[move[0]][move[1]] = playerTurn
             playerTurn = not playerTurn
        if self.gameOver():
            return False
        else:
            return self.playerXWon(True)


    def gameOver(self):
        for row in self.board:
            for col in row:
                if col == None:
                    return False
        return True
    
    def playerXWon(self, x: bool):
        for i in range(3):
            if self.rowNX(i, x):
                return True
        for i in range(3):
            if self.colNX(i, x):
                return True
        downDiag = self.board[0][0] == x and self.board[1][1] == x and self.board[2][2] == x
        upDiag = self.board[2][0] == x and self.board[1][1] == x and self.board[0][2] == x
        return downDiag or upDiag
    


    def rowNX(self, n: int, x: bool):
        for col in self.board[n]:
            if col != x:
                return False
        return True
    def colNX(self, n: int, x: bool):
        for row in self.board:
            if row[n] != x:
                return False
        return True

Tests:
### START TESTS ###
if True:  # pragma: no cover
    # Game tests
    gameOver = Game(None, None)
    gameOver.board = [[True, False, True],
                      [False, True, False],
                      [True, False, True]]
    assert gameOver.gameOver()

    player1Won = Game(None, None)
    player1Won.board = [[True, True, True],
                        [True, True, True],
                        [True, True, True]]
    assert player1Won.playerXWon(True)

    player2Won = Game(None, None)

    player2Won.board = [[False, False, False],
                        [False, False, False],
                        [False, False, False]]
    assert player2Won.playerXWon(False)
    downDiag = Game(None, None)
    downDiag.board = [[True, False, False],
                      [False, True, False],
                      [False, False, True]]
    assert downDiag.playerXWon(True)
    upDiag = Game(None, None)
    upDiag.board = [[False, False, True],
                    [False, True, False],
                    [True, False, False]]
    assert upDiag.playerXWon(True)

    cs = CornerStrategy()
    b = [[None for _ in range(3)] for _ in range(3)]
    assert cs.returnMove(b) == (0, 0)
    b[0][0] = True
    assert cs.returnMove(b) == (0, 2)
    b[0][2] = True
    assert cs.returnMove(b) == (2, 0)
    b[2][0] = True
    assert cs.returnMove(b) == (2, 2)
    b[2][2] = True

    try:
        cs.returnMove(b)
    except:
        assert True
    else:
        assert False

    gs = GoodStrategy()
    b = [[None for _ in range(3)] for _ in range(3)]
    try:
        gs.returnMove(b)
        gs.returnMove(b)
        gs.returnMove(b)
        gs.returnMove(b)
    except Exception:
        assert True


    # Did not change Game test
    import inspect

    assert inspect.getsource(Game).strip() == '''class Game:
    def __init__(self, player1: Strategy, player2: Strategy):
        self.playerOne = player1
        self.playerTwo = player2
        self.board = [[None for _ in range(3)] for _ in range(3)]

    def player1Won(self):
        playerTurn = True
        while not self.playerXWon(True) and not self.playerXWon(False) and not self.gameOver():
             strat = self.playerOne if playerTurn else self.playerTwo
             move = strat.returnMove(self.board)
             self.board[move[0]][move[1]] = playerTurn
             playerTurn = not playerTurn
        if self.gameOver():
            return False
        else:
            return self.playerXWon(True)


    def gameOver(self):
        for row in self.board:
            for col in row:
                if col == None:
                    return False
        return True
    
    def playerXWon(self, x: bool):
        for i in range(3):
            if self.rowNX(i, x):
                return True
        for i in range(3):
            if self.colNX(i, x):
                return True
        downDiag = self.board[0][0] == x and self.board[1][1] == x and self.board[2][2] == x
        upDiag = self.board[2][0] == x and self.board[1][1] == x and self.board[0][2] == x
        return downDiag or upDiag
    


    def rowNX(self, n: int, x: bool):
        for col in self.board[n]:
            if col != x:
                return False
        return True
    def colNX(self, n: int, x: bool):
        for row in self.board:
            if row[n] != x:
                return False
        return True'''.strip()

    # Followed prompt test
    g = Game(GoodStrategy(), CornerStrategy())
    assert g.player1Won()
    g = Game(CornerStrategy(), GoodStrategy())
    assert not g.player1Won()

    gameOver = Game(GoodStrategy(), CornerStrategy())
    gameOver.board = [[True, False, True],
                      [False, True, False],
                      [True, False, True]]
    assert gameOver.gameOver()
    assert not gameOver.player1Won()