首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >变板尺寸的Tic-Tac-脚趾(第1部分)

变板尺寸的Tic-Tac-脚趾(第1部分)
EN

Code Review用户
提问于 2019-04-20 05:52:14
回答 1查看 2.8K关注 0票数 6

第2部分:变板尺寸的Tic-Tac-脚趾(第2部分)

我用python创建了一个Tic-Tac-Toe程序,它可以选择将标准的3x3板更改为NxN板。我也创造了简单,艰难和疯狂的人工智能。

请帮助我改进代码和删除bug(如果有的话)

代码:

代码语言:javascript
复制
import os
from random import randint

cls = lambda: os.system('CLS')  # Works only in command console.

# Random names
names = [
    'Jacob', 'Michael',
    'Joshua', 'Ethan', 'Matthew', 'Daniel',
    'Christopher', 'Andrew', 'Anthony', 'William',
    'Joseph', 'Alexander', 'David', 'Ryan',
    'Noah', 'James', 'Nicholas', 'Tyler',
    'Logan', 'John', 'Christian', 'Jonathan',
    'Nathan', 'Benjamin', 'Samuel', 'Dylan',
    'Brandon', 'Gabriel', 'Elijah', 'Aiden',
    'Angel', 'Jose', 'Zachary', 'Caleb',
    'Jack', 'Jackson', 'Kevin', 'Gavin',
    'Mason', 'Isaiah', 'Austin', 'Evan',
    'Luke', 'Aidan', 'Justin', 'Jordan',
    'Robert', 'Isaac', 'Landon', 'Jayden',
    'Thomas', 'Cameron', 'Connor', 'Hunter',
    'Jason', 'Diego', 'Aaron', 'Bryan',
    'Owen', 'Lucas', 'Charles', 'Juan',
    'Luis', 'Adrian', 'Adam', 'Julian',
    'Alex', 'Sean', 'Nathaniel', 'Carlos',
    'Jeremiah', 'Brian', 'Hayden', 'Jesus',
    'Carter', 'Sebastian', 'Eric', 'Xavier',
    'Brayden', 'Kyle', 'Ian', 'Wyatt',
    'Chase', 'Cole', 'Dominic', 'Tristan',
    'Carson', 'Jaden', 'Miguel', 'Steven',
    'Caden', 'Kaden', 'Antonio', 'Timothy',
    'Henry', 'Alejandro', 'Blake', 'Liam',
    'Richard', 'Devin', 'Riley', 'Jesse',
    'Seth', 'Victor', 'Brady', 'Cody',
    'Jake', 'Vincent', 'Bryce', 'Patrick',
    'Colin', 'Marcus', 'Cooper', 'Preston',
    'Kaleb', 'Parker', 'Josiah', 'Oscar',
    'Ayden', 'Jorge', 'Ashton', 'Alan',
    'Jeremy', 'Joel', 'Trevor', 'Eduardo',
    'Ivan', 'Kenneth', 'Mark', 'Alexis',
    'Omar', 'Cristian', 'Colton', 'Paul',
    'Levi', 'Damian', 'Jared', 'Garrett',
    'Eli', 'Nicolas', 'Braden', 'Tanner',
    'Edward', 'Conner', 'Nolan', 'Giovanni',
    'Brody', 'Micah', 'Maxwell', 'Malachi',
    'Fernando', 'Ricardo', 'George', 'Peyton',
    'Grant', 'Gage', 'Francisco', 'Edwin',
    'Derek', 'Max', 'Andres', 'Javier',
    'Travis', 'Manuel', 'Stephen', 'Emmanuel',
    'Peter', 'Cesar', 'Shawn', 'Jonah',
    'Edgar', 'Dakota', 'Oliver', 'Erick',
    'Hector', 'Bryson', 'Johnathan', 'Mario',
    'Shane', 'Jeffrey', 'Collin', 'Spencer',
    'Abraham', 'Leonardo', 'Brendan', 'Elias',
    'Jace', 'Bradley', 'Erik', 'Wesley',
    'Jaylen', 'Trenton', 'Josue', 'Raymond',
    'Sergio', 'Damien', 'Devon', 'Donovan',
    'Dalton', 'Martin', 'Landen', 'Miles',
    'Israel', 'Andy', 'Drew', 'Marco',
    'Andre', 'Gregory', 'Roman', 'Ty',
    'Jaxon', 'Avery', 'Cayden', 'Jaiden',
    'Roberto', 'Dominick', 'Rafael', 'Grayson',
    'Pedro', 'Calvin', 'Camden', 'Taylor',
    'Dillon', 'Braxton', 'Keegan', 'Clayton',
    'Ruben', 'Jalen', 'Troy', 'Kayden',
    'Santiago', 'Harrison', 'Dawson', 'Corey',
    'Maddox', 'Leo', 'Johnny', 'Kai',
    'Drake', 'Julio', 'Lukas', 'Kaiden',
    'Zane', 'Aden', 'Frank', 'Simon',
    'Sawyer', 'Marcos', 'Hudson', 'Trey'
]

# Dummy Variable
start = 0

# Essential Variables:
player = 'Player'  # Player name
board_type = 2  # Board Type (1 or 2)

board = [['', '', ''], ['', '', ''], ['', '', '']]  # The TicTacToe board
win_board = [['', '', ''], ['', '', ''], ['', '', '']]  # Traces the win (if any) of 'board'


X = 'X'  # Character for player 1
O = 'O'  # Character for player 2

size = 3  # Size of 'board'


def countWins(p1, p2):
    """

    p1: Player 1
    p2: Player 2

    Counts the wins possible in the current move for 'p1'

    """

    count = 0  # Keeps count of wins possible

    for i in range(size):
        for j in range(size):
            if board[i][j] != p1 and board[i][j] != p2:
                copy = board[i][j]  # A dummy variable to restore 'board[i][j]'
                board[i][j] = p1

                if win(p1) == 1:
                    count += 1

                board[i][j] = copy

    return count


def get_insane_AI_move(ai, pl, x=0, name=''):
    """

    ai: ai character
    pl: player character
    x: dummy variable
    name: ai name

    The best AI

    Follows all the tips and checks for moves leading to multiple wins constantly

    """

    for i in range(size):
        for j in range(size):
            if board[i][j] != ai and board[i][j] != pl:
                copy = board[i][j]
                board[i][j] = ai

                if win(ai) == 1 or tie() == 1:
                    if x:
                        print(name + ' Moved To Grid', i * size + j + 1)
                    return

                board[i][j] = copy

    for i in range(size):
        for j in range(size):
            if board[i][j] != ai and board[i][j] != pl:
                copy = board[i][j]
                board[i][j] = pl

                if win(pl) == 1 or tie() == 1:
                    board[i][j] = ai
                    if x:
                        print(name + ' Moved To Grid', i * size + j + 1)
                    return
                board[i][j] = copy

    wins2 = []
    l = 0

    for i in range(size):
        for j in range(size):
            if board[i][j] != ai and board[i][j] != pl:
                copy = board[i][j]
                board[i][j] = ai

                if countWins(ai, pl) > 1:
                    l += 1
                    r = [i, j]
                    wins2.append(r)

                board[i][j] = copy

    if l:
        m = wins2[randint(0, 1000) % l]
        board[m[0]][m[1]] = ai
        if x:
            print(name + ' Moved To Grid', m[0] * size + m[1] + 1)
        return

    l = 0

    pos_centers = [[i, j] for i in range(size) for j in range(size)
                   if (i in [0, size - 1]) == (j in [0, size - 1]) == False]

    centers = []

    for i in range(len(pos_centers)):
        x = pos_centers[i][0]
        y = pos_centers[i][1]

        if board[x][y] != ai and board[x][y] != pl:
            centers.append(pos_centers[i])
            l += 1

    if l:
        r = centers[randint(1, 1000) % l]
        board[r[0]][r[1]] = ai

        if x:
            print(name + ' Moved To Grid', r[0] * size + r[1] + 1)

        return

    l1 = 0
    l2 = 0

    pos_edges = [[0, 0], [0, size - 1], [size - 1, 0], [size - 1, size - 1]]
    edges = []

    for i in range(len(pos_edges)):
        x = pos_edges[i][0]
        y = pos_edges[i][1]

        if board[x][y] != ai and board[x][y] != pl:
            edges.append(pos_edges[i])
            l1 += 1

    if l1:
        r = edges[randint(1, 1000) % l1]
        board[r[0]][r[1]] = ai

        if x:
            print(name + ' Moved To Grid', r[0] * size + r[1] + 1)

        return

    pos_middles = [[i, j] for i in range(size) for j in range(size)
                   if (i in [0, size - 1]) != (j in [0, size - 1])]

    middles = []

    for i in range(len(pos_middles)):
        x = pos_middles[i][0]
        y = pos_middles[i][1]

        if board[x][y] != ai and board[x][y] != pl:
            middles.append(pos_middles[i])
            l2 += 1

    r = middles[randint(1, 1000) % l2]
    board[r[0]][r[1]] = ai

    if x:
        print(name + ' Moved To Grid', r[0] * size + r[1] + 1)

    return


def get_hard_AI_move(ai, pl, x=0, name=''):
    """

    A medium AI

    Can only look ahead 1 move

    """

    for i in range(size):
        for j in range(size):
            if board[i][j] != ai and board[i][j] != pl:
                copy = board[i][j]
                board[i][j] = ai

                if win(ai) == 1 or tie() == 1:
                    if x:
                        print(name + ' Moved To Grid', i * size + j + 1)
                    return

                board[i][j] = copy

    for i in range(size):
        for j in range(size):
            if board[i][j] != ai and board[i][j] != pl:
                copy = board[i][j]
                board[i][j] = pl

                if win(pl) == 1 or tie() == 1:
                    board[i][j] = ai

                    if x:
                        print(name + ' Moved To Grid', i * size + j + 1)

                    return

                board[i][j] = copy

    l = 0

    possible = [[i, j] for i in range(size) for j in range(size)]

    available = []

    for i in range(len(possible)):
        x = possible[i][0]
        y = possible[i][1]

        if board[x][y] != ai and board[x][y] != pl:
            available.append(possible[i])
            l += 1

    r = available[randint(1, 1000) % l]

    board[r[0]][r[1]] = ai

    if x:
        print(name + ' Moved To Grid', r[0] * size + r[1] + 1)

    return


def get_easy_AI_move(ai, pl, x=0, name=''):
    """

    An easy AI

    Moves randomly

    """

    l = 0

    possible = [[i, j] for i in range(size) for j in range(size)]

    available = []

    for i in range(len(possible)):
        x = possible[i][0]
        y = possible[i][1]

        if board[x][y] != ai and board[x][y] != pl:
            available.append(possible[i])
            l += 1

    r = available[randint(1, 1000) % l]
    board[r[0]][r[1]] = ai

    if x:
        print(name + ' Moved To Grid', r[0] * size + r[1] + 1)
    return


def get_user_move(p1, p2):
    """ Gets user input and processes it """

    g = int(input(f'Please Enter Grid Number (1 ~ {size * size}): ')) - 1

    x = g // size
    y = g % size

    if x >= size or y >= size or board[x][y] == p1 or board[x][y] == p2:
        print('Please Enter A Valid Move')
        get_user_move(p1, p2)
        return

    print(player + ' Moved To Grid', g + 1)

    board[x][y] = p1
    print()


def get_win(p):
    """ Traces the win into 'win_board' """

    for i in range(size):
        # Rows
        if all(board[i][j] == p for j in range(size)):
            for j in range(size):
                win_board[i][j] = p
            return

        # Columns
        if all(board[j][i] == p for j in range(size)):
            for j in range(size):
                win_board[j][i] = p
            return

    # Diagonals
    if all(board[i][i] == p for i in range(size)):
        for i in range(size):
            win_board[i][i] = p
        return

    if all(board[i][-(i + 1)] == p for i in range(size)):
        for i in range(size):
            win_board[i][-(i + 1)] = p
        return

    ## Returns in every case as multiple wins might be traced out


def printBoard1():
    """ Prints board type 1 """

    for i in range(size - 1):
        print('      ' + '|       ' * (size - 1))
        print(end='  ')

        for j in range(size - 1):
            print(board[i][j], end='   |   ')

        print(board[i][-1])
        print('      ' + '|       ' * (size - 1))
        print('------' + '--------' * (size - 1))
        '      |       '

    print('      ' + '|       ' * (size - 1))
    print(end='  ')

    for j in range(size - 1):
        print(board[-1][j], end='   |   ')

    print(board[-1][-1])
    print('      ' + '|       ' * (size - 1))

    print()


def printBoard2():
    """ Prints board type 2 """

    for i in range(size - 1):
        for j in range(size - 1):
            print(board[i][j], end=' | ')
        print(board[i][-1])
        print('---' * size + '-' * (size - 3))

    for j in range(size - 1):
        print(board[-1][j], end=' | ')

    print(board[-1][-1])
    print()


def printWin(p):
    """ Prints 'win_board' at board type 2"""

    get_win(p)

    for i in range(size - 1):
        for j in range(size - 1):
            print(win_board[i][j], end=' | ')
        print(win_board[i][-1])
        print('---' * size + '-' * (size - 2))

    for j in range(size - 1):
        print(win_board[-1][j], end=' | ')

    print(win_board[-1][-1])
    print()


def getRandomName():
    """ Gets random names from 'names' """

    name = names[randint(1, 1000) % 250]
    return name


def helper():
    """ Help section containing Rules, Tips and Credits """

    print()

    print('B for Back\n')
    print('1. Rules')
    print('2. Tips')
    print('3. Credits')

    option = input('\nPlease Enter Your Option: ').lower()
    print()

    if option == 'b': return
    if option == '1': rules()
    if option == '2': tips()
    if option == '3': about()

    input('Enter To Continue . . . ')
    print()
    helper()


def about():  ## Couldn't name this credits as there's a built-in name
    print('This Game Of Tic-Tac-Toe Is Created By Srivaths')
    print('If You Are Unfamiliar With This Game, Please Read The Rules And Tips')
    print('Enjoy!!\n')


def changeName():
    """ Changes player name: 'player' """

    global player
    player = input('Please Enter Your Name: ')


def changeBoard():
    """ Changes board type: 'board_type' """

    global board_type

    print()
    print('B for Back\n')
    print('1.')
    printBoard1()
    print('2.\n')
    printBoard2()

    print()

    option = input('\nPlease Enter Your Option: ')

    if option == 'b' or option == 'B':
        return

    if option == '1': board_type = 1
    if option == '2': board_type = 2


def changeCharacters():
    """ Changes characters: 'X', 'O' """

    global X, O

    print()
    X = input('Please Enter Character For Player 1 (currently ' + X + '): ')
    O = input('Please Enter Character For Player 2 (currently ' + O + '): ')


def changeSize():
    """ Changes board size: 'size' """

    global size
    size = int(input('Please Enter Size: '))
    initialize()


def settings():
    """ Settings """

    print()

    print('B for Back\n')
    print('1. Change Name')
    print('2. Change Size')
    print('3. Change Board')
    print('4. Change Characters')
    option = input('\nPlease Enter Your Option: ').lower()

    if option == 'b':
        return

    if option == '1': changeName()
    if option == '2': changeSize()
    if option == '3': changeBoard()
    if option == '4': changeCharacters()

    print()
    settings()


def main_menu():
    """ The main menu """

    global start

    # cls()
    print()

    if start == 0:
        intro()
        start = 1
        main_menu()
        return

    print('Hello ' + player)

    print('\nQ for Quit\n')
    print('1. Help')
    print('2. Settings')
    print('3. Play')
    option = input('\nPlease Enter Your Option: ')

    if option == '1':
        helper()

    if option == '2':
        settings()

    if option == '3':
        initialize()
        play('X', 'O')

    if option == 'q' or option == 'Q':
        print('Thanks For Playing!\n')
        return

    print()
    main_menu()


def rules():
    """ Basic rules """

    print('1. In Tic-Tac-Toe, there are 2 players \n\tand their characters are X and O respectively')
    print('2. Any row or column or diagonal filled tith the same character is a win')
    print('3. A board where there are no moves left is a tie')
    print('4. You are not allowed to place characters over another')
    print('5. The playes must play in alternate turns, starting with X')
    print()


def tips():
    """ Basic tips """

    print('1. Always try and capture the center')
    print('2. Next try to capture the edges')
    print('3. Occupy the edges only if necessary')
    print('4. Be aware of immediate moves')
    print('5. Try the easy bot to get the hang of the game')
    print()


def intro():
    """ Introduction """

    global board_type

    initialize()

    print('Hello Player', end=', ')

    changeName()

    print('\nHello ' + player + ', Welcome To The Game Of Tic-Tac-Toe!!')

    know = input('Are You Familiar With The Game? (y / n): ').lower()

    if know == 'n':
        print('\nFirst A Little Introduction To The Rules: \n')
        rules()
        print('\nNext A Few Tips: \n')
        tips()
        print('\nAnd That\'s ALL!!!\n')
        input('Enter To Continue . . . ')
        print('\n')

    print('\nPlease Pick Your Board Preference: \n')

    print('1.')
    printBoard1()
    print('2.\n')
    printBoard2()

    print()

    option = input('Please Enter Your Option: ')

    if option == '1': board_type = 1
    if option == '2': board_type = 2

    print()

    print('Change Characters Via [Main Menu -> Settings -> Change Characters]')
    print()

    print('Here You Must Try Your Luck Against Three Levels!!\n')
    print('1. Easy')
    print('2. Hard')
    print('3. Insane')
    print()

    print('Can YOU Beat Them ALL????')
    print('Let\'s See....\n')

    input('Enter To Continue . . . ')


def play(p1, p2):
    """

    The play area

    p1: Player 1
    p2: Player 2

    """

    print()
    initialize()

    computer = getRandomName()

    print('1. Easy')
    print('2. Hard')
    print('3. Insane')

    print()

    level = int(input('Please Enter Level: '))

    print()

    while computer == player:
        computer = getRandomName()

    print('\t\t' + player + ' VS ' + computer + '\n\n')

    c = randint(0, 1)

    pl = p1
    ai = p2

    if c == 0:
        ai = p1
        pl = p2

        print('\n' + computer + ' Goes First!\n\n')

    else:
        print('\n' + player + ' Goes First!\n\n')
        if board_type == 1:
            printBoard1()
        else:
            printBoard2()

    d = 0

    while True:
        t = d % 2

        if t == c:
            if level == 1: get_easy_AI_move(ai, pl, 1, computer)
            if level == 2: get_hard_AI_move(ai, pl, 1, computer)
            if level == 3: get_insane_AI_move(ai, pl, 1, computer)

            if board_type == 1:
                printBoard1()
            else:
                printBoard2()

            if win(ai):
                print(computer + ' Wins!\n')
                print('Below Is How ' + computer + ' Won\n\n')
                printWin(ai)
                break

        else:
            get_user_move(pl, ai)

            if board_type == 1:
                printBoard1()
            else:
                printBoard2()

            if win(pl):
                print(player + ' Wins!')
                print('Below Is How ' + player + ' Won\n')
                printWin(pl)
                break

        if tie():
            print('Tie!')
            break

        d += 1

    play_again(p1, p2)


def initialize():
    """ Resets the board """

    global board, win_board

    board = [[' ' for _ in range(size)] for __ in range(size)]
    win_board = [[' ' for _ in range(size)] for __ in range(size)]


def play_again(p1, p2):
    """ Gets input from the player asking if they want to play again """

    option = input('Would You Like To Play Again? (y(yes) / n(no) / m(Main Menu): ').lower()

    if option == 'y':
        play(p1, p2)
    elif option == 'n':
        return
    elif option == 'm':
        return
    else:
        print('\nPlease Enter a Valid Option')
        play_again(p1, p2)


def win(p):
    """ Checks for win """

    if any(all(board[i][j] == p for j in range(size)) for i in range(size)):
        return True
    if any(all(board[j][i] == p for j in range(size)) for i in range(size)):
        return True
    if all(board[i][i] == p for i in range(size)):
        return True
    if all(board[i][-(i + 1)] == p for i in range(size)):
        return True

    return False


def tie():
    """ Checks for tie """

    return all(all(j in [X, O] for j in i) for i in board)


main_menu()

如果程序在CMD控制台上运行,而不是在IDE控制台上运行,将会更具有交互性。如果您确实在CMD控制台上运行该程序,则可以在您喜欢的地方将cls()添加到程序中。

EN

回答 1

Code Review用户

回答已采纳

发布于 2019-04-21 20:43:20

欢迎,并感谢你提供了一个有趣的主题。我喜欢游戏难度等级的想法!

也就是说,你的代码太长了。不是“太长的评论”,只是“太长”。

Moar函数!(&生成器!)

考虑到这一点:

代码语言:javascript
复制
for i in range(size):
    for j in range(size):
        if board[i][j] != ai and board[i][j] != pl:
            copy = board[i][j]
            board[i][j] = ai

            if win(ai) == 1 or tie() == 1:
                if x:
                    print(name + ' Moved To Grid', i * size + j + 1)
                return

            board[i][j] = copy

(取自疯狂的人工智能函数)

现在考虑这一点:

代码语言:javascript
复制
for i in range(size):
    for j in range(size):
        if board[i][j] != ai and board[i][j] != pl:
            copy = board[i][j]
            board[i][j] = pl

            if win(pl) == 1 or tie() == 1:
                board[i][j] = ai
                if x:
                    print(name + ' Moved To Grid', i * size + j + 1)
                return
            board[i][j] = copy

现在考虑这一点:

代码语言:javascript
复制
for i in range(size):
    for j in range(size):
        if board[i][j] != ai and board[i][j] != pl:
            copy = board[i][j]
            board[i][j] = ai

            if countWins(ai, pl) > 1:
                l += 1
                r = [i, j]
                wins2.append(r)

            board[i][j] = copy

这三个块来自相同的函数,但它们是不同的行。你能看到里面有多少重复吗?

  • (i, j)行和列上迭代
  • 获得X/O/空值
  • 检查单元格是否为空
  • 复制先验值
  • 检查胜负或平局
  • 将坐标转换为网格位置
  • 打印“移动到”消息
  • 把牢房设置好。

这些代码中有多少可以用函数替换?这些代码中有多少可以用生成器替换?

对行/列范围进行迭代是有价值的,但是最好直接迭代(i, j)元组--这并不像您不使用另一个元组。

更好的方法是只迭代表示空单元格的元组:

代码语言:javascript
复制
for i, j in board.empty_cells():

或者,如果你还没有学过课程:

代码语言:javascript
复制
for i, j in empty_cells(board):

为此,您将在一个yield中使用发生器函数关键字。

接下来,你在做什么?在第一个循环中,你会问玩家是会赢还是会和下一个动作打成平局。事实证明,“领带”对你来说意味着“每个地点都会被填满”,这是令人失望的,但事实却是如此。

我建议“即将结婚”意味着“只有一个空白”,这是一个全球真实的情况。你不需要经常检查它。

另一方面,所有set/check/unset逻辑似乎都是编写函数的好地方:

代码语言:javascript
复制
def would_win_if_moved_to(player, location) -> bool:
    ''' Return true if player would win if their glyph was placed at location. '''
    # TODO
    pass

如果您有这个函数,您的两个顶部循环如下所示:

代码语言:javascript
复制
for locn in empty_cells(board):
    if would_win_if_moved_to(ai, locn):
        # Go for the win!
        move_to(locn)

for locn in empty_cells(board):
    if would_win_if_moved_to(player, locn):
        # Block opponent win!
        move_to(locn)

当然,通过将这些行封装到它们自己的函数中,可以使您的逻辑更加清晰:

代码语言:javascript
复制
for locn in moves_to_win(ai):
    return locn
for locn in moves_to_block_win(player):
    return locn

在最后一个循环中,您将添加到一个列表中。这是一个很好的指示,表明您可以使用列表理解,如果只剩下的循环可以写得更短。你可以这样做:

代码语言:javascript
复制
wins2 = [locn for locn in empty_cells(board) if wins_after_move_to(ai, locn) > 1]

你只需要一个函数,它可以先做你的“推测”动作。

Moar文档字符串!

docstring语法的另一种用法是用一个字符串跨越多行。你应该用这个作为你的巨大文本块,比如规则和提示。而不是这样做:

代码语言:javascript
复制
print("line 1")
print("line 2")
print("line 3")

你可以这样做:

代码语言:javascript
复制
text = """
    line 1
    line 2
    line 3
""".strip('\n')
print(textwrap.dedent(text))

使用Python附带的textwrap模块。

我认为,如果您做了这些更改,您的代码将变得更小。你会准备好接受另一次审查的。;-)

票数 5
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/217767

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档