from tkinter import *
import random
tk = Tk()
tk.wm_title("Battleship")
switch = True
game_over = False
labels = []
class player:
def __init__(self):
self.switchuser = False
self.placedships = []
self.bombed = []
self.sunk = []
self.ship_sizes = [5, 4, 3, 3, 2]
self.direction = 'v'
self.player_ocean = []
temp = []
for i in range(10):
for y in range(10):
temp += [0]
self.player_ocean += [temp]
temp = []
self.selectedCoord = [0, 0] # [0] = x coord, [1] = y coord
self.attack_selection = [0, 0]
self.hits = []
self.misses = []
def selectPlacement(self, event, key):
'''
initialise column and row index
condition for different directions
check if ship placed is off grid or clashes with another ship
place ship
add to self.placedships
remove ship from availiable ships to cycle to next ship
'''
clear = True
col = self.selectedCoord[0]
row = self.selectedCoord[1]
print(self.selectedCoord)
if self.direction == 'v':
v_range_start = row - self.ship_sizes[0] + 1
if v_range_start >= 0: # check if ship will be off the grid
for cell in range(v_range_start, row + 1): # check if the ship clashes with existing ships
if [cell, col] in self.placedships:
clear = False
break
if clear == True:
for y in range(v_range_start, row + 1):
self.player_ocean[y][col] = 1
self.placedships.append([y, col])
self.ship_sizes.remove(self.ship_sizes[0])
refresh_ocean('place')
elif self.direction == 'h':
h_range_end = col + self.ship_sizes[0]
if 10 > h_range_end:
for cell in range(col, h_range_end):
if [row, cell] in self.placedships:
clear = False
break
if clear == True:
for x in range(col, h_range_end):
self.player_ocean[row][x] = 1
self.placedships.append([row, x])
self.ship_sizes.remove(self.ship_sizes[0])
refresh_ocean('place')
def selectAttack(self, event):
col = self.attack_selection[0]
row = self.attack_selection[1]
if [row, col] in ai.ai_placedships:
if [row, col] not in self.hits:
self.hits.append([row, col])
print('hit')
else:
if [row, col] not in self.misses:
self.misses.append([row, col])
# if str(type(event)) == 'tkinter.Event':
# return True
refresh_ocean('attackselection')
ai.ai_attack()
# forming tkinter window
def baseGrid():
gridLabel_player = Label(tk,
text="Your grid \nA B C D E F G H I J ")
gridLabel_player.grid(row=0, column=0)
gridLabel_ai = Label(tk,
text="AI's grid \nA B C D E F G H I J ")
gridLabel_ai.grid(row=0, column=1)
tk.player_canvas = Canvas(tk, height=300, width=300, highlightbackground='black', highlightthickness=0.5)
tk.ai_canvas = Canvas(tk, height=300, width=300, highlightbackground='black', highlightthickness=0.5)
tk.player_canvas.grid(row=1, column=0, padx=50)
tk.ai_canvas.grid(row=1, column=1, padx=50)
for x in range(10):
for y in range(10):
tk.player_canvas.create_rectangle(x * 30, y * 30, 300, 300, fill='white')
tk.ai_canvas.create_rectangle(x * 30, y * 30, 300, 300, fill='white')
def moveShip(event, key):
print(player1.selectedCoord)
if event.keysym == 'Down':
if key == 'place':
if player1.selectedCoord[1] != 9:
player1.selectedCoord[1] += 1
elif key == 'attackselection':
if player1.attack_selection[1] != 9:
player1.attack_selection[1] += 1
elif event.keysym == 'Up':
if key == 'place':
if player1.selectedCoord[1] != 0:
player1.selectedCoord[1] -= 1
elif key == 'attackselection':
if player1.attack_selection[1] != 0:
player1.attack_selection[1] -= 1
elif event.keysym == 'Left':
if key == 'place':
if player1.selectedCoord[0] != 0:
player1.selectedCoord[0] -= 1
elif key == 'attackselection':
if player1.attack_selection[0] != 0:
player1.attack_selection[0] -= 1
elif event.keysym == 'Right':
if key == 'place':
if player1.selectedCoord[0] != 9:
player1.selectedCoord[0] += 1
elif key == 'attackselection':
if player1.attack_selection[0] != 9:
player1.attack_selection[0] += 1
for y in range(10):
for x in range(10):
if key == 'place':
if [y, x] == player1.selectedCoord or [x, y] in player1.placedships:
player1.player_ocean[x][y] = 1
else:
player1.player_ocean[x][y] = 0
# elif key == 'attackselection':
# if [y,x] == player1.attack_selection or [x,y] in player1.hits:
# ai.ai_ocean[x][y] = 1
# else:
# ai.ai_ocean[x][y] = 0
refresh_ocean(key)
def changedir(event):
if player1.direction == 'v':
player1.direction = 'h'
elif player1.direction == 'h':
player1.direction = 'v'
def refresh_ocean(key):
print('function call')
for y in range(10):
for x in range(10):
colour = 'white'
if key == 'place':
if [y, x] in player1.placedships:
colour = 'green'
if [x, y] == player1.selectedCoord:
colour = 'yellow'
tk.player_canvas.itemconfig(
tk.player_canvas.create_rectangle(x * 30, y * 30, (x + 1) * 30, (y + 1) * 30, fill=colour))
elif key == 'attackselection':
# print('miss',ai.AI_miss,'\nhits',ai.destroyed_cords,'\nships',player1.placedships)
if [y, x] in player1.placedships:
tk.player_canvas.itemconfig(
tk.player_canvas.create_rectangle(x * 30, y * 30, (x + 1) * 30, (y + 1) * 30), fill='green')
if [y, x] in ai.AI_miss:
tk.player_canvas.itemconfig(
tk.player_canvas.create_rectangle(x * 30, y * 30, (x + 1) * 30, (y + 1) * 30), fill='gray')
if [y, x] in ai.destroyed_cords:
tk.player_canvas.itemconfig(
tk.player_canvas.create_rectangle(x * 30, y * 30, (x + 1) * 30, (y + 1) * 30), fill='red')
if [y, x] in player1.hits:
colour = 'red'
if [y, x] in player1.misses:
colour = 'gray'
if [x, y] == player1.attack_selection:
colour = 'yellow'
tk.ai_canvas.itemconfig(
tk.ai_canvas.create_rectangle(x * 30, y * 30, (x + 1) * 30, (y + 1) * 30), fill=colour)
# else:
# tk.player_canvas.itemconfig(
# tk.player_canvas.create_rectangle(x * 30, y * 30, (x + 1) * 30, (y + 1) * 30, fill='white'))
def attackGui():
tk.bind("<Key>", lambda event: moveShip(event, 'attackselection'))
tk.bind("<Return>", lambda event: player1.selectAttack(event))
class Ai(player):
def __init__(self):
# = [[[9,0], [9, 1], [9, 2], [9, 3], [9, 4]], [[9, 5], [9, 6]]]
# player1.placedships = [[[9, 0], [8, 0], [7, 0], [6, 0], [5, 0]], [[4, 0], [3, 0]]]
self.destroyed_ships = []
self.destroyed_cords = []
self.AI_miss = []
self.fmove_check = True
self.mdirection = 1
self.ai_placedships = []
self.directions = ['v', 'h']
self.ship_sizes = [5, 4, 3, 3, 2]
self.currentcoord = [0,0]
def ai_attack(self):
def first_move(self):
self.missed = 0
self.AIx = random.randint(0, 9)
self.AIy = random.randint(0, 9)
self.AIsel_cords = [self.AIy, self.AIx]
while self.AIsel_cords in self.AI_miss or self.AIsel_cords in self.destroyed_cords:
self.AIx = random.randint(0, 9)
self.AIy = random.randint(0, 9)
self.AIsel_cords = [self.AIy, self.AIx]
self.currentcoord = self.AIsel_cords
if self.AIsel_cords in player1.placedships:
# self.ship_check = ship
self.destroyed_cords.append(player1.placedships.pop(player1.placedships.index(self.AIsel_cords)))
self.fmove_check = False
else:
self.AI_miss.append(self.AIsel_cords)
def fol_move(self):
checked = False
self.entered = False
# direction check
if self.mdirection > 4:
self.mdirection = 1
# up
elif self.mdirection == 1 and self.AIy - 1 >= 0:
self.entered = True
self.AIy -= 1
self.currentcoord[0] -= 1
# to find whether the cords are part of a ship
if [self.AIy, self.AIx] in player1.placedships:
# if [self.AIx, self.AIy] in self.ship_check:
# self.destroyed_cords.append(ship.pop(ship.index([self.AIx, self.AIy])))
# else:
# self.destroyed_cords +=
self.destroyed_cords.append(
player1.placedships.pop(player1.placedships.index([self.AIy, self.AIx])))
checked = True
# add the cords that are not part of a ship into AI_miss
else:
self.AI_miss.append([self.AIy, self.AIx])
self.AIy, self.AIx = self.AIsel_cords[0], self.AIsel_cords[1]
self.mdirection += 1
self.missed += 1
# left
elif self.mdirection == 2 and self.AIx - 1 >= 0:
self.entered = True
self.AIx -= 1
self.currentcoord[1] -= 1
# to find whether the cords are part of a ship
if [self.AIy, self.AIx] in player1.placedships:
self.destroyed_cords.append(
player1.placedships.pop(player1.placedships.index([self.AIy, self.AIx])))
checked = True
# add the cords that are not part of a ship into AI_miss
else:
self.AI_miss.append([self.AIy, self.AIx])
self.AIy, self.AIx = self.AIsel_cords[0], self.AIsel_cords[1]
self.mdirection += 1
self.missed += 1
# down
elif self.mdirection == 3 and self.AIy + 1 <= 9:
self.entered = True
self.AIy += 1
self.currentcoord[0] += 1
# to find whether the cords are part of a ship
if [self.AIy, self.AIx] in player1.placedships:
self.destroyed_cords.append(
player1.placedships.pop(player1.placedships.index([self.AIy, self.AIx])))
checked = True
# add the cords that are not part of a ship into AI_miss
else:
self.AI_miss.append([self.AIy, self.AIx])
self.AIy, self.AIx = self.AIsel_cords[0], self.AIsel_cords[1]
self.mdirection += 1
self.missed += 1
# right
elif self.mdirection == 4 and self.AIx + 1 <= 9:
self.entered = True
self.AIx += 1
self.currentcoord[1] += 1
# to find whether the cords are part of a ship
if [self.AIy, self.AIx] in player1.placedships:
self.destroyed_cords.append(
player1.placedships.pop(player1.placedships.index([self.AIy, self.AIx])))
checked = True
# add the cords that are not part of a ship into AI_miss
else:
self.AI_miss.append([self.AIy, self.AIx])
self.AIy, self.AIx = self.AIsel_cords[0], self.AIsel_cords[1]
self.mdirection += 1
self.missed += 1
elif not self.entered:
self.AIy, self.AIx = self.AIsel_cords[0], self.AIsel_cords[1]
self.mdirection += 1
if self.missed == 2:
self.missed = 0
self.fmove_check = True
if self.fmove_check:
first_move(self)
elif not self.fmove_check:
fol_move(self)
if not self.entered:
fol_move(self)
# print('\n', self.destroyed_cords)
# print(player1.placedships)
# print(self.AI_miss)
def ai_place(self):
print(len(ai.ship_sizes))
ai_dir = random.choice(self.directions)
col = random.randint(0, 9)
row = random.randint(0, 9)
clear = True
if ai_dir == 'v':
v_range_start = row - self.ship_sizes[0] + 1
if v_range_start >= 0: # check if ship will be off the grid
for cell in range(v_range_start, row + 1): # check if the ship clashes with existing ships
if [cell, col] in self.ai_placedships:
clear = False
break
if clear == True:
for y in range(v_range_start, row + 1):
self.ai_placedships.append([y, col])
self.ship_sizes.remove(self.ship_sizes[0])
elif ai_dir == 'h':
h_range_end = col + self.ship_sizes[0]
if 10 > h_range_end:
for cell in range(col, h_range_end):
if [row, cell] in self.ai_placedships:
clear = False
break
if clear == True:
for x in range(col, h_range_end):
self.ai_placedships.append([row, x])
self.ship_sizes.remove(self.ship_sizes[0])
attackinstructionLabel = Label(tk, text="Arrow keys to move selection"
"\nEnter to shoot at selected cell"
"\nGrey cells are missed ships"
"\nRed cells are hit ships")
attackinstructionLabel.grid(row=2)
instructionLabel = Label(tk, text="Arrow keys to move selection"
"\nRight shift to change ship orientation"
"\nEnter to place ship"
"\nYellow cells means that you are selecting a cell to place the next ship in"
"\nGreen cells show placed ships")
instructionLabel.grid(row=2)
tk.bind("<Key>", lambda event: moveShip(event, 'place'))
tk.bind("<Shift_R>", changedir)
tk.bind("<Return>", lambda event: player1.selectPlacement(event, 'place'))
base = baseGrid()
player1 = player()
ai = Ai()
while True:
tk.update()
tk.update_idletasks()
if len(player1.ship_sizes) != 0:
if len(ai.ship_sizes) != 0:
ai_place = ai.ai_place()
refresh_ocean('place')
else:
if (sorted(player1.hits) != sorted(ai.ai_placedships)) and len(player1.placedships) != 0:
attack = attackGui()
instructionLabel.destroy()
refresh_ocean('attackselection')
else:
popup = Tk()
p_label = Label(popup,text='GAME OVER')
popup.after(5000,p_label.destroy())
tk.destroy()
i tried taking out all the code run during the while loop that creates additional things in the program but it still gets noticeably laggy. the only thing i can identify right now which may be the problem is the refresh_ocean
function which is the only thing that deals with the gui in the while loop but it uses the itemconfig method which shouldnt add on anything to the gui