Tenho tentado aprender Python e voltar a programar depois de muitos anos, então tentei um jogo de batalha naval bem básico no terminal. Tive muitos obstáculos que superei, mas fiquei em dúvida sobre minhas funções generate_(n)ship(). Tentei escrever o código de forma que eu iniciasse a função com um savestate que assumisse o estado atual do tabuleiro, para que, mais tarde, se a função decidisse tentar colocar uma nave em outro espaço que já tivesse uma, ela pudesse desistir, redefinir o savestate e tentar novamente até que escolhesse e colocasse (n) espaços em branco.
import random
board = []
pboard = []
row = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]
col = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
def print_board(board, prnt):
for x in range(10):
board.append(["O"] * 10)
if prnt == True:
print(" ".join(board[x]))
def print_pboard(pboard):
for x in range(10):
pboard.append(["O"] * 10)
print(" ".join(pboard[x]))
print_board(board, False)
print_pboard(pboard)
print("Welcome to Battleship!")
def get_coords():
coord = str(input("Enter coordinates (e.g., A1): ").upper())
if len(coord) == 3:
if coord[2] == "0":
return row.index(coord[0]), 9
if len(coord) != 2:
print("Invalid input. Please enter a letter followed by a number (e.g., A1).")
return get_coords()
if coord[0] not in row or not coord[1].isdigit() or int(coord[1]) < 1 or int(coord[1]) > 10:
print("Invalid coordinates. Please try again.")
return get_coords()
return row.index(coord[0]), int(coord[1]) - 1
def generate_5ship(board):
orientation = random.choice(["horizontal", "vertical"])
savestate = board.copy()
if orientation == "horizontal":
x = [random.randint(4, 9)]
y = [random.randint(0, 9)]
for i in range(1, 5):
x.append(x[0] - i)
else:
x = [random.randint(0, 9)]
y = [random.randint(4, 9)]
for i in range(1, 5):
y.append(y[0] - i)
pos = x, y
if len(pos[0]) > len(pos[1]):
for i in range(5):
inty = sum(pos[1])
if board[x[i]][inty] == "O":
board[x[i]][inty] = "5"
else:
board = savestate.copy()
return generate_5ship(board)
else:
for i in range(5):
intx = sum(pos[0])
if board[intx][y[i]] == "O":
board[intx][y[i]] = "5"
else:
board = savestate.copy()
return generate_5ship(board)
def generate_4ship(board):
orientation = random.choice(["horizontal", "vertical"])
savestate = board.copy()
print("board")
print_board(board, True)
print("savestate")
print_board(savestate, True)
if orientation == "horizontal":
x = [random.randint(3, 9)]
y = [random.randint(0, 9)]
for i in range(1, 4):
x.append(x[0] - i)
else:
x = [random.randint(0, 9)]
y = [random.randint(4, 9)]
for i in range(1, 4):
y.append(y[0] - i)
pos = x, y
if len(pos[0]) > len(pos[1]):
for i in range(4):
inty = sum(pos[1])
if board[x[i]][inty] == "O":
board[x[i]][inty] = "4"
else:
print("board before reverting")
print_board(board, True)
board = savestate.copy()
print("board after reverting")
print_board(board, True)
return generate_4ship(board)
else:
for i in range(4):
intx = sum(pos[0])
if board[intx][y[i]] == "O":
board[intx][y[i]] = "4"
else:
board = savestate.copy()
return generate_4ship(board)
def generate_3ship(board):
orientation = random.choice(["horizontal", "vertical"])
savestate = board.copy()
if orientation == "horizontal":
x = [random.randint(2, 9)]
y = [random.randint(0, 9)]
for i in range(1, 3):
x.append(x[0] - i)
else:
x = [random.randint(0, 9)]
y = [random.randint(2, 9)]
for i in range(1, 3):
y.append(y[0] - i)
pos = x, y
if len(pos[0]) > len(pos[1]):
for i in range(3):
inty = sum(pos[1])
if board[x[i]][inty] == "O":
board[x[i]][inty] = "3"
else:
board = savestate.copy()
return generate_3ship(board)
else:
for i in range(3):
intx = sum(pos[0])
if board[intx][y[i]] == "O":
board[intx][y[i]] = "3"
else:
board = savestate.copy()
return generate_3ship(board)
def generate_2ship(board):
orientation = random.choice(["horizontal", "vertical"])
savestate = board.copy()
if orientation == "horizontal":
x = [random.randint(1, 9)]
y = [random.randint(0, 9)]
for i in range(1, 2):
x.append(x[0] - i)
else:
x = [random.randint(0, 9)]
y = [random.randint(1, 9)]
for i in range(1, 2):
y.append(y[0] - i)
pos = x, y
if len(pos[0]) > len(pos[1]):
for i in range(2):
inty = sum(pos[1])
if board[x[i]][inty] == "O":
board[x[i]][inty] = "2"
else:
board = savestate.copy()
return generate_2ship(board)
else:
for i in range(2):
intx = sum(pos[0])
if board[intx][y[i]] == "O":
board[intx][y[i]] = "2"
else:
board = savestate.copy()
return generate_2ship(board)
def is_hit(x, y):
if board[x][y] == "X":
print("Hit!")
pboard[x][y] = "X"
board[x][y] = "O"
elif pboard[x][y] == "X":
print("Already hit!")
else:
print("Miss!")
pboard[x][y] = "M"
def fill_board():
generate_5ship(board)
generate_4ship(board)
generate_4ship(board)
generate_3ship(board)
generate_3ship(board)
generate_3ship(board)
generate_2ship(board)
generate_2ship(board)
generate_2ship(board)
generate_2ship(board)
def get_win_condition():
for i in range(10):
for j in range(10):
if board[i][j] == "X":
return False
return True
def get_misses():
misses = 0
for i in range(10):
for j in range(10):
if pboard[i][j] == "M":
misses += 1
return misses
def get_hits_left():
hits = 0
for i in range(10):
for j in range(10):
if board[i][j] == "X":
hits += 1
return hits
fill_board()
while True:
print_board(board, True)
x, y = get_coords()
is_hit(x, y)
print_pboard(pboard)
print("Hits left: " + str(get_hits_left()))
if get_win_condition() == True:
print("You win!")
print("You missed " + str(get_misses()) + " times!")
break
print("Game Over!")
Esta é a função que tenho usado para depurar. O que eu esperava que ela fizesse era salvar o estado atual do tabuleiro, escolher uma coordenada "semente", construir o navio (neste caso, 4 de comprimento) verificando a string "O", substituindo-a por "X" para indicar um navio e, em seguida, subtraindo do lado correspondente da tupla com base na sua decisão de ser posicionado vertical ou horizontalmente. Substituí os X pelos seus respectivos comprimentos, novamente para fins de depuração. Quando a função começa a colidir com um navio preexistente no tabuleiro, espero que a instrução else substitua o valor do tabuleiro recém-alterado de volta ao seu estado salvo e tente novamente. Em vez disso, ela posiciona o máximo possível, para quando percebe que não pode, ignora a instrução else completamente e retorna ao início da função.
PS: Se você está se perguntando por que calculei a soma para intx ou inty, por algum motivo não consegui descobrir como recuperar o único item da lista e armazená-lo como um inteiro. Calcular a soma da lista foi a primeira coisa que funcionou.
def generate_4ship(board):
orientation = random.choice(["horizontal", "vertical"])
savestate = board.copy()
print("board")
print_board(board, True)
print("savestate")
print_board(savestate, True)
if orientation == "horizontal":
x = [random.randint(3, 9)]
y = [random.randint(0, 9)]
for i in range(1, 4):
x.append(x[0] - i)
else:
x = [random.randint(0, 9)]
y = [random.randint(4, 9)]
for i in range(1, 4):
y.append(y[0] - i)
pos = x, y
if len(pos[0]) > len(pos[1]):
for i in range(4):
inty = sum(pos[1])
if board[x[i]][inty] == "O":
board[x[i]][inty] = "4"
else:
print("board before reverting")
print_board(board, True)
board = savestate.copy()
print("board after reverting")
print_board(board, True)
return generate_4ship(board)
else:
for i in range(4):
intx = sum(pos[0])
if board[intx][y[i]] == "O":
board[intx][y[i]] = "4"
else:
board = savestate.copy()
return generate_4ship(board)
Acredito que o seu problema (ou pelo menos parte dele) esteja no
.copy()
método de lista que você está usando para salvar o estado do tabuleiro. Esse método cria o que chamamos de cópia superficial de uma lista, ou seja, os elementos na cópia apontam para os elementos da lista original. Como a lista que você está copiando contém outras listas, quaisquer modificações que você fizer nessas listas aninhadas serão refletidas em todas as cópias.Você quer o que é chamado de cópia profunda, que não mantém essas referências, e pode fazer isso com a biblioteca de cópias integrada:
A partir daí, substitua todas as instâncias de
variable_name.copy()
pordeepcopy(variable_name)
.Resolvi o problema. Minha lógica estava correta, mas a variável board estava sendo referenciada globalmente, mas escrita apenas localmente. A solução é um pouco confusa, mas é um programa curto e simples e funcionará, apesar de saber que é uma prática ruim. Removi o parâmetro board da função generate, bem como o argumento ao chamá-lo. Os argumentos de retorno dentro da função também foram apagados. Simplesmente declarei a variável board global dentro da função, para que ela pudesse ser escrita globalmente.