Совершенствования и оптимизации крестики-нолики игра, написанная в Python 3


Я преподаю себя Питон3 в течение приблизительно недели теперь, и я решил поставить свои навыки для теста, написав простые крестики-нолики. Вот что я придумал:

#!/usr/bin/env python3

import random

board = [0]*9

def resetBoard():
    for index, i in enumerate(board):
        board[index] = 0

def printBoard():
    counter = 0
    s = ""
    print("|-----|-----|-----|")
    for index, i in enumerate(board):
        s += "|  "
        if(board[index] == 0):
            s += str(index)
        if(board[index] == 1):
            s+= "x"
        if(board[index] == 2):
            s += "o"
        s += "  "
        counter += 1
        if(counter == 3):
            s += "|"
            print(s)
            print("|-----|-----|-----|")
            s = ""
            counter = 0

def getPlayers():
    return input("1 or 2 Players?")

def moveSquare(xOro, ai):
    square = 0
    flag = 0
    while(board[square] != 0 or flag == 0):
        if(ai == 0):
            if(xOro == 1):
                square = eval(input("Choose a square to place X: "))
            if(xOro == 2):
                square = eval(input("Choose a square to place O: "))
        if(ai == 1):
            square = random.randint(0, 8)
        flag = 1
    board[square] = xOro
    print("\n\n")

def checkForWin():
    if(board[0] == board[1] and board[1] == board[2] and board[2] != 0):
        return 1
    if(board[3] == board[4] and board[4] == board[5] and board[5] != 0):
        return 1
    if(board[6] == board[7] and board[7] == board[8] and board[8] != 0):
        return 1
    if(board[0] == board[3] and board[3] == board[6] and board[6] != 0):
        return 1
    if(board[1] == board[4] and board[4] == board[7] and board[7] != 0):
        return 1
    if(board[2] == board[5] and board[5] == board[8] and board[8] != 0):
        return 1
    if(board[0] == board[4] and board[4] == board[8] and board[8] != 0):
        return 1
    if(board[2] == board[4] and board[4] == board[6] and board[6] != 0):
        return 1
    for index, i in enumerate(board):
        if(i  == 0):
            break
        if(index == 8):
            return 2
    return 0

def gameOver():
    return eval(input("\nPress 0 and then ENTER to play again!"))

# Main
random.seed()
quit = 0
while(quit == 0):
    players = 0
    win = 0
    while(players != 1 and players != 2):
        players = eval(getPlayers())
    resetBoard()
    game = 0 #game = 1 when game is finished
    while(game == 0): #game loop
        printBoard()
        moveSquare(1, 0)
        game = checkForWin()
        if(game > 0):
            win = game
            break
        printBoard()
        if(players == 1):
            moveSquare(2, 1)
        if(players == 2):
            moveSquare(2, 0)
        game = checkForWin()
        if(game > 0):
            win = game + 1
            break
    if(win == 1):
        print("\nX Wins!")
    if(win == 2):
        print("\nO Wins!")
    if(win == 3):
        print("\n The game is a draw!")
    quit = gameOver()
exit()

Меня интересует следующее мой код:

  • Как я могу сделать мой код более мелкие и более оптимизирован?
  • В игре отображаются "о выигрыше", а не "игра-ничья!" и я не уверен, почему.
  • Как я могу санировать Ввод, чтобы предотвратить сбои если в строке введено вместо целого числа (например, при выборе квадрата на доске)?
  • Любые дополнительные очки, чтобы улучшить мой код или общие советы, которые помогут мне стать лучше программист Python.


1986
3
задан 16 октября 2011 в 05:10 Источник Поделиться
Комментарии
2 ответа

#!/usr/bin/env python3

import random

board = [0]*9

Имея глобальные переменные, что изменение является плохой идеей. Вы бы лучше положить эту переменную и методов внутри класса.

def resetBoard():
for index, i in enumerate(board):
board[index] = 0

Вы могли бы также сделать Совет[:] = [0] * 9

def printBoard():
counter = 0

В Python вы редко нужно следить за счетчиками такой

    s = ""

В Python, вы должны избегать струны здания, добавляя различные части вместе

    print("|-----|-----|-----|")
for index, i in enumerate(board):
s += "| "
if(board[index] == 0):

Смысл перечислять, что я это значение в доску. Использовать его, а затем refetching его здесь.

            s += str(index)
if(board[index] == 1):
s+= "x"
if(board[index] == 2):
s += "o"
s += " "

Путем построения строки таким образом, его трудно понять, какие именно строки.

        counter += 1
if(counter == 3):

Вместо счетчика использовать `если индекс % 3 == 2: Также вам не нужны скобки

            s += "|"
print(s)
print("|-----|-----|-----|")
s = ""
counter = 0

Весь ваш алгоритм здесь оказывается более сложной, потому что вы не перебираете ячейки, а строки. Ваш алгоритм был бы гораздо более естественным с рядами

def getPlayers():
return input("1 or 2 Players?")

Разве это не должно возвращать int, а не строку?

def moveSquare(xOro, ai):

имена переменных рекомендуется следовать x_or_o как соглашение об именовании

    square = 0
flag = 0

Если это логический флаг, использовать ложные не 0

    while(board[square] != 0 or flag == 0):

Не нужны скобки в выражении

        if(ai == 0):

Снова используйте значение true/false для boolean флаги. Нет необходимости в скобки

            if(xOro == 1):
square = eval(input("Choose a square to place X: "))
if(xOro == 2):
square = eval(input("Choose a square to place O: "))

Избегайте магических чисел, как 1 и 2 для обозначения х и О. В этом случае, я бы, наверное, пройти 'X' или 'O' строк.

        if(ai == 1):

Использовать еще:

            square = random.randint(0, 8)
flag = 1

Флаг действительно ужасное имя переменной, потому что я не знаю, что это означает. Булевы флаги, как правило, уродливые решения

    board[square] = xOro
print("\n\n")

def checkForWin():
if(board[0] == board[1] and board[1] == board[2] and board[2] != 0):
return 1

Не используйте магические числа, как это. Вместо того, чтобы создавать какие-то глобальные константы для возврата или что-то

    if(board[3] == board[4] and board[4] == board[5] and board[5] != 0):
return 1
if(board[6] == board[7] and board[7] == board[8] and board[8] != 0):
return 1
if(board[0] == board[3] and board[3] == board[6] and board[6] != 0):
return 1
if(board[1] == board[4] and board[4] == board[7] and board[7] != 0):
return 1
if(board[2] == board[5] and board[5] == board[8] and board[8] != 0):
return 1
if(board[0] == board[4] and board[4] == board[8] and board[8] != 0):
return 1
if(board[2] == board[4] and board[4] == board[6] and board[6] != 0):
return 1

Много дублирования здесь.

    for index, i in enumerate(board):
if(i == 0):
break
if(index == 8):
return 2
return 0

def gameOver():
return eval(input("\nPress 0 and then ENTER to play again!"))

Eval-это немного опасно, потому что пользователь может поставить на все что угодно.

# Main

Вы должны ваша главная логика в функции

random.seed()
quit = 0
while(quit == 0):

Сделать довольно булевой переменной

    players = 0
win = 0
while(players != 1 and players != 2):
players = eval(getPlayers())
resetBoard()
game = 0 #game = 1 when game is finished

Игра становится 1, когда игра закончена, кажется, назад

    while(game == 0): #game loop
printBoard()
moveSquare(1, 0)
game = checkForWin()
if(game > 0):
win = game
break

Не много втак игры == 0 выше если вы просто собираюсь разбить нафик

        printBoard()
if(players == 1):
moveSquare(2, 1)
if(players == 2):
moveSquare(2, 0)
game = checkForWin()
if(game > 0):
win = game + 1
break

Вижу, как две части петли очень похожи? Предполагает, что вы должны избавиться от дублирования.

    if(win == 1):
print("\nX Wins!")
if(win == 2):
print("\nO Wins!")
if(win == 3):
print("\n The game is a draw!")
quit = gameOver()

Имея сайт Gameover() возвращает, является ли или не вы должны бросить, кажется, сбивает с толку. Название не означает, что

exit()

Нет смысла в призывах бросить курить, программа собирается уволить в любом случае

И просто чтобы показать, как я делаю это:

#!/usr/bin/env python3
import random

WINNING_LINES = [
[0, 1, 2],
[3, 4, 5],
[5, 6, 7],

[0, 3, 5],
[1, 4, 6],
[2, 5, 7],

[0, 4, 7],
[2, 4, 5]
]

STALEMATE = '!'

class TicTacToe(object):
def __init__(self):
self.board = [' '] * 9

def print(self):
# make a copy of the board
cells = self.board[:]

# put a digit in every blank cell
for index, cell in enumerate(cells):
if cell == ' ':
cells[index] = str(index)

# put spaces around the cells
cells = [' %s ' % cell for cell in cells]

# print the actual board
print('|---|---|---|')
for start in range(0, 9, 3):
row = cells[start:start+3]
print('|', '|'.join(row), '|', sep = '')
print('|---|---|---|')

def move_square(self, player, ai):
square = -1
while square < 0 or square >= 9 or self.board[square] != ' ':
if ai:
square = random.randrange(9)
else:
question = "Choose a square to place {}:".format(player.upper())
square_text = input(question)
print(square_text)
try:
square = int(square_text)
except ValueError:
# user didn't enter a number
square = -1
self.board[square] = player
print("\n\n")

def status(self):
# check for a win
for a, b, c in WINNING_LINES:
if self.board[a] == self.board[b] and self.board[b] == self.board[c] and self.board[a] != ' ':
return self.board[a]

if not any(cell == ' ' for cell in self.board):
return STALEMATE
return ' '

def get_players():
while True:
text = input("1 or 2 players")
try:
value = int(text)
except ValueError:
pass
else:
if 0 < value < 3:
return value

def play_again():
return input("\nPress 0 and then ENTER to play again!") == '0'

GAME_OVER_MESSAGES = {
'X' : 'X wins!',
'O' : 'O wins!',
'!' : 'The game is a draw!'
}

def main():

while True:

# create a list to denote whether
# a player is AI
ai_players = [False, False]
if get_players() == 1:
ai_players[1] = True

tic_tac_toe = TicTacToe()
player_turn = 0
player_codes = 'XY'
while tic_tac_toe.status() == ' ':
tic_tac_toe.print()
tic_tac_toe.move_square(player_codes[player_turn], ai_players[player_turn])
player_turn = (player_turn + 1) % 2

print(GAME_OVER_MESSAGES[tic_tac_toe.status()])

if not play_again():
break

if __name__ == '__main__':
main()

5
ответ дан 16 октября 2011 в 05:10 Источник Поделиться

Пару замечаний по общему стилю:

while(players != 1 and players != 2):
players = eval(getPlayers())

Я ожидаю getPlayers (), чтобы получить информацию об игроке и делать каких-либо обработки ошибок.
Таким образом, в основной цикл это должен быть один вызов:

players = getPlayers()

У вас есть много мест, где вы сделать то же самое двумя различными способами:

    if(players == 1):
moveSquare(2, 1)
if(players == 2):
moveSquare(2, 0)

Можно упростить эту как

    moveSquare(2, players - 1)

Же самое происходит и здесь:

if(win == 1):
print("\nX Wins!")
if(win == 2):
print("\nO Wins!")
if(win == 3):
print("\n The game is a draw!")

Может быть намного аккуратнее, такой:

print(result[win])   # Now just define the result array.

Вашу проблему с печатью неправильная вещь на ничью-это здесь:

    if(game > 0):
win = game
break

Заметьте, что это отличается от вашего во-вторых, проверьте здесь

    game = checkForWin()
if(game > 0):
win = game + 1
break

Так checkForWin() возвращает 0 для победителя, 1 лауреат и 2 для ничьей.
Первая проверка выше приведет к победе 1 или 2 даже за ничью, в то время как вторая проверка будет результат в 2 или 3 (что наверное правильно).

Что вы действительно хотите сделать, это сделать checkForWin() возвращают результат, который вы ищете.

0:  for no winner
1: for player one
2: for player two
3: for a draw.

Вы даже можете оптимизировать checkForWin (), передавая игроку, который сделал последний ход и позиция, которую они играют в (как выигрышный ход включают их последний ход).

#!/usr/bin/env python3

import random

# Put the ID of the square in each square for easy printing.
# We will put 'X' or 'O' in the square as we make each move
def resetBoard(board):
for index, i in enumerate(board):
board[index] = index

# Print the board
def printBoard(board):
s = ""
for index, i in enumerate(board):
s += "| %s " % i
if((index % 3) == 2):
print("|-----|-----|-----|\n%s|" % s)
s = ""
print("|-----|-----|-----|")

# Function to get the human mve
def humanMove(playerToMove, board):

print "Human Move (%s)" % playerToMove
return eval(raw_input("Choose a square to place %s: " % playerToMove))

# Function to get the AI move
def aiMove(playerToMove, board):

print "AI Move (%s)" % playerToMove
return random.randint(0, 8)

def moveSquare(playerToMove, players, board):
# Get the move from the cuttrny player
# Note this passes weather they are an 'X' or an 'O'
# as a parameter.
FirstTry = False
square = 0
while(board[square] == 'X' or board[square] == 'O' or FirstTry):
square = players[playerToMove](playerToMove, board)
FirstTry = False;
board[square] = playerToMove # Puts an 'X' or an 'O' on the board
print("\n\n")

#
# This function returns a dictionary with move move functions.
# So on 'X' move call the result{'X'} function to get a move
# So we have to decide how to fill the dictionary based on user input.
def getPlayers():
players = 0;
while players != 1 and players != 2:
players = input("1 or 2 Players?")

if players == 2:
# Two players so both elements call the humanMove function
return {'X': humanMove, 'O': humanMove}

if players == 1:
human = '?'
while human != 'X' and human != 'O':
human = raw_input("Do you want to play X or O");
if human == 'X':
# Human is playing X ai is playing O so functions set appropriately.
return {'X': humanMove, 'O': aiMove}
else:
return {'X': aiMove, 'O': humanMove}

#
# Return 'X' for winner is X
# Return 'O' for winner is O
# Return 'D' for a draw
# Return 'R' for game is still running.
def checkForWin(board):
if(board[0] == board[1] and board[1] == board[2] and board[2] != 0):
return board[0]
if(board[3] == board[4] and board[4] == board[5] and board[5] != 0):
return board[3]
if(board[6] == board[7] and board[7] == board[8] and board[8] != 0):
return board[6]
if(board[0] == board[3] and board[3] == board[6] and board[6] != 0):
return board[0]
if(board[1] == board[4] and board[4] == board[7] and board[7] != 0):
return board[1]
if(board[2] == board[5] and board[5] == board[8] and board[8] != 0):
return board[2]
if(board[0] == board[4] and board[4] == board[8] and board[8] != 0):
return board[0]
if(board[2] == board[4] and board[4] == board[6] and board[6] != 0):
return board[2]

# No winner yet
# decide if the game is still running.
for index, i in enumerate(board):
if(i != 'X' or i != 'O'):
return 'R'

# No empty squares so it must be a draw.
return 'D'

def gameOver():
return raw_input("\nPress 0 and then ENTER to play again!")

# Main
random.seed()
quit = "0"
board = [0]*9

while(quit == "0"):
resetBoard(board)
winner = 'R'
players = getPlayers()
playerOrder = ['X', 'O']
currentPlayer = 0;
while True:
printBoard(board)

moveSquare(playerOrder[currentPlayer], players, board)
winner = checkForWin(board)
if winner != 'R':
break

currentPlayer = (currentPlayer + 1) % 2

result = { 'X': "X Wins!",'O': "O Wins!", 'D': "The game is a draw!"}
print "\n%s\n" % result[winner];

quit = gameOver()

1
ответ дан 16 октября 2011 в 05:10 Источник Поделиться