我用python创建了一个Tic-Tac-Toe程序,它可以选择将标准的3x3板更改为NxN板。我也创造了简单,艰难和疯狂的人工智能。
请帮助我改进代码和删除bug(如果有的话)
代码:
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()添加到程序中。
发布于 2019-04-21 20:43:20
欢迎,并感谢你提供了一个有趣的主题。我喜欢游戏难度等级的想法!
也就是说,你的代码太长了。不是“太长的评论”,只是“太长”。
考虑到这一点:
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现在考虑这一点:
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)行和列上迭代这些代码中有多少可以用函数替换?这些代码中有多少可以用生成器替换?
对行/列范围进行迭代是有价值的,但是最好直接迭代(i, j)元组--这并不像您不使用另一个元组。
更好的方法是只迭代表示空单元格的元组:
for i, j in board.empty_cells():或者,如果你还没有学过课程:
for i, j in empty_cells(board):为此,您将在一个yield中使用发生器函数关键字。
接下来,你在做什么?在第一个循环中,你会问玩家是会赢还是会和下一个动作打成平局。事实证明,“领带”对你来说意味着“每个地点都会被填满”,这是令人失望的,但事实却是如此。
我建议“即将结婚”意味着“只有一个空白”,这是一个全球真实的情况。你不需要经常检查它。
另一方面,所有set/check/unset逻辑似乎都是编写函数的好地方:
def would_win_if_moved_to(player, location) -> bool:
''' Return true if player would win if their glyph was placed at location. '''
# TODO
pass如果您有这个函数,您的两个顶部循环如下所示:
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)当然,通过将这些行封装到它们自己的函数中,可以使您的逻辑更加清晰:
for locn in moves_to_win(ai):
return locn
for locn in moves_to_block_win(player):
return locn在最后一个循环中,您将添加到一个列表中。这是一个很好的指示,表明您可以使用列表理解,如果只剩下的循环可以写得更短。你可以这样做:
wins2 = [locn for locn in empty_cells(board) if wins_after_move_to(ai, locn) > 1]你只需要一个函数,它可以先做你的“推测”动作。
docstring语法的另一种用法是用一个字符串跨越多行。你应该用这个作为你的巨大文本块,比如规则和提示。而不是这样做:
print("line 1")
print("line 2")
print("line 3")你可以这样做:
text = """
line 1
line 2
line 3
""".strip('\n')
print(textwrap.dedent(text))使用Python附带的textwrap模块。
我认为,如果您做了这些更改,您的代码将变得更小。你会准备好接受另一次审查的。;-)
https://codereview.stackexchange.com/questions/217767
复制相似问题