import dos

# A small complete app using classes, mutable lists and real D/OS input/rendering.
class Game:
    def __init__(self):
        self.board = [0, 0, 0, 0, 0, 0, 0, 0, 0]
        self.reset()

    def reset(self):
        for i in range(9):
            self.board[i] = 0
        self.moves = 0
        self.ended = False
        self.message = "Your turn"

    def wins(self, player):
        for i in range(3):
            row = i * 3
            if self.board[row] == player and self.board[row + 1] == player and self.board[row + 2] == player:
                return True
            if self.board[i] == player and self.board[i + 3] == player and self.board[i + 6] == player:
                return True
        if self.board[0] == player and self.board[4] == player and self.board[8] == player:
            return True
        if self.board[2] == player and self.board[4] == player and self.board[6] == player:
            return True
        return False

    def place(self, index, player):
        self.board[index] = player
        self.moves += 1
        if self.wins(player):
            self.ended = True
            self.message = "You win!" if player == 1 else "Computer wins!"
        elif self.moves == 9:
            self.ended = True
            self.message = "A draw!"

    def computer(self):
        # Finish a winning line, block a threat, then prefer center and corners.
        for player in [2, 1]:
            for i in range(9):
                if self.board[i] == 0:
                    self.board[i] = player
                    win = self.wins(player)
                    self.board[i] = 0
                    if win:
                        self.place(i, 2)
                        return
        for i in [4, 0, 2, 6, 8, 1, 3, 5, 7]:
            if self.board[i] == 0:
                self.place(i, 2)
                return

    def play(self, index):
        if self.ended or index < 0 or index > 8:
            return
        if self.board[index] != 0:
            return
        self.place(index, 1)
        if not self.ended:
            self.computer()

    def draw(self):
        dos.intent("GFX", "BEGIN_FRAME")
        dos.intent("GFX", "FILL_RECT", 0, 0, 320, 200, 4226)
        dos.intent("GFX", "DRAW_TEXT", 16, 14, "TIC / TAC / TOE", 1, -1, 4226)
        dos.intent("GFX", "DRAW_TEXT", 182, 16, "d/Python", 1, 2047, 4226)
        for i in range(9):
            x = 16 + (i % 3) * 42
            y = 48 + (i // 3) * 42
            dos.intent("GFX", "FILL_RECT", x, y, 38, 38, 6371)
            label = str(i + 1)
            color = -21163
            if self.board[i] == 1:
                label = "X"
                color = -736
            elif self.board[i] == 2:
                label = "O"
                color = 2047
            dos.intent("GFX", "DRAW_TEXT", x + 14, y + 12, label, 1, color, 6371)
        dos.intent("GFX", "DRAW_TEXT", 162, 62, self.message, 1, -1, 4226)
        dos.intent("GFX", "DRAW_TEXT", 162, 91, "You: X", 1, -736, 4226)
        dos.intent("GFX", "DRAW_TEXT", 162, 111, "Computer: O", 1, 2047, 4226)
        dos.intent("GFX", "DRAW_TEXT", 162, 145, "R: new game", 1, -21163, 4226)
        dos.intent("GFX", "DRAW_TEXT", 16, 183, "Click a square or press 1-9", 1, -21163, 4226)
        dos.intent("GFX", "PRESENT")

game = Game()
pointer_down = False

@dos.on("WINDOW_OPEN")
def open_window():
    dos.service("SET_TITLE", "d/Python — Tic Tac Toe")

@dos.on("WINDOW_DRAW")
def draw_window():
    game.draw()

@dos.on("ON_KEY")
def on_key(key):
    if key == 82 or key == 114:
        game.reset()
    elif key >= 49 and key <= 57:
        game.play(key - 49)
    dos.service("INVALIDATE")

@dos.on("ON_POINTER")
def on_pointer(x, y, buttons):
    global pointer_down
    down = buttons % 2 == 1
    if down and not pointer_down:
        if x >= 16 and x < 138 and y >= 48 and y < 170:
            column = (x - 16) // 42
            row = (y - 48) // 42
            if (x - 16) % 42 < 38 and (y - 48) % 42 < 38:
                game.play(row * 3 + column)
                dos.service("INVALIDATE")
    pointer_down = down
