# Battleships 2K16
# Completed 11/11/16, 13:00
# Developed by Tommy Kong
# This is a Battleships game.
# It allows players to:
# Play against each other
# Play against a computer
# Practice shooting ships
import random, time
# Class for creating a grid
class Grid:
# Create gird of width and height
def __init__(self,width,height):
self.grid = []
self.pieces = [[0],[1],[2],["miss"],["hit"]]
for y in range(height):
row = []
for x in range(width):
row.append("")
self.grid.append(row)
# Add piece to given coordinates
def addPiece(self,piece,side):
for pieceSet in self.pieces:
if pieceSet[0] == side:
pieceSet.append(piece)
for coord in piece:
self.grid[coord[1]][coord[0]] = side
# Function checks if the grid still has pieces of a certain side
def isDefeated(self,side):
for row in self.grid:
for piece in row:
if piece == side:
return False
return True
# Display the grid for the user
def show(self):
# Adding the top coordinates
print("-" * 45)
line = ""
for i in range(10):
line += "| " + str(i) + " "
print(line + "| |")
print("-" * 45)
# Adding the individual cells
for y in range(len(self.grid)):
line = ""
for x in self.grid[y]:
if x == "":
line += "| - "
elif x == 0:
line += "| - "
elif x == 1:
line += "| - "
elif x == 2:
line += "| - "
elif x == "miss":
line += "| X "
elif x == "hit":
line += "| O "
# Adding side coordinates
line += "| " + str(y) + " |\n"
for x in self.grid[y]:
line+="----"
line += "-----"
print(line)
# Returns if a grid is empty
def isEmpty(self,x,y):
if self.grid[y][x] == "":
return True
else:
return False
# Returns the value inside a coord
def getCoordValue(self,x,y):
return self.grid[y][x]
# Class which handles the battleships game
class Battleships:
# Allow the user to choose from 3 options
def __init__(self):
while True:
print("Welcome to Battleships!")
print("You can - ")
print("[1] PvP\n[2] PvE\n[3]Just Shoot Ships")
decision = 0
while decision < 1 or decision > 3:
try:
decision = int(input("What would you like to do: "))
except:
decision = 0
if decision == 1:
self.PVP()
elif decision == 2:
self.PVE()
elif decision == 3:
self.JSS()
# Adds a ship
def addShip(self,grid,side,length):
orientation = 0
while orientation < 1 or orientation > 2:
try:
orientation = int(input("Would you like the ship to be horizontal [1] or vertical [2]: "))
except:
orientation = 0
if orientation == 1:
rootCoord = self.inputCoord(10-(length),9)
elif orientation == 2:
rootCoord = self.inputCoord(9,10-(length))
ship = []
# Whilst valid ship has not been added
while True:
currentShip = []
# Add coords depending on length
for i in range(length):
if orientation == 1:
currentShip.append([rootCoord[0]+i,rootCoord[1]])
elif orientation == 2:
currentShip.append([rootCoord[0],rootCoord[1]+i])
# Test that the coords are not filled already
validShip = True
for coord in currentShip:
if grid.isEmpty(coord[0],coord[1]) == False:
# If any coords are filled then the ship is invalid
validShip = False
print("There are already ships existing there!")
return self.addShip(grid,side,length)
# If ship is valid then stop trying and return ship coords
if validShip:
ship = currentShip
return ship
# Function returns list of ship lengths that has been sunk
def getSunkShips(self,grid,side):
# List of sunk ships
sunkShips = []
# Go through the pieces array in grid object
for ship in range(1,len(grid.pieces[side])):
sunkStatus = []
# For each ship coordinate in a ship
for shipCoord in grid.pieces[side][ship]:
# If the coordinate can be found in the hit list then that part has been sunk
sunk = False
for hitCoord in range(1,len(grid.pieces[4])):
if shipCoord == grid.pieces[4][hitCoord][0]:
sunk = True
break
sunkStatus.append(sunk)
# Go through the sunk parts and if all of it is sunk then the ship is sunk
sunkShip = True
for status in sunkStatus:
if status == False:
sunkShip = False
break
if sunkShip == True:
sunkShips.append(ship+1)
return sunkShips
# Method for when the user wants to play against the computer
def PVE(self):
# Create grids
grids = [Grid(10,10),Grid(10,10)]
print("Now you are going to add your ships.")
# Add ships for player 1
print("Player 1 add ships.")
print("You input the root coordinate of the ship. They extend to the right or down, depending on orientation.")
# Add ships of length 2 - 6
for shipLength in range(5):
print("Add ship of length {}".format(shipLength+2))
ship = self.addShip(grids[0],1,shipLength+2)
grids[0].addPiece(ship,1)
input("Press enter to continue...")
self.clearScreen()
print("Okay, the grids are set!")
self.clearScreen()
# Add ships for computer
grids[1].grid = self.makeShips(grids[1],0,[1,1,1,1,1])
turn = 1
# Lists of coords the computer should shoot next
compWaitList = [[]]
# Coords the computer has tried
compShotList = []
compSunkShips = []
compPreviousHits = []
# While there are ships on both side
while grids[0].isDefeated(1) == False and grids[1].isDefeated(0) == False:
# If it is player 1's turn
if turn == 1:
print("Player 1's turn to shoot.")
grids[1].show()
validMove = False
while validMove == False:
# Get shot input and try to shoot
# If shot is not invalid then update the grid
shot = self.inputCoord(9,9)
potentialGrid = self.shoot(grids[1],0,shot)
if potentialGrid != "invalid":
grids[1].grid = potentialGrid
validMove = True
else:
continue
input("Press enter to continue.")
self.clearScreen()
print("Current grid for Player 1.")
grids[1].show()
# Check game is won or not
if grids[1].isDefeated(0) == True:
self.clearScreen()
print("Player 1 wins!")
input("Press enter to continue...")
self.clearScreen()
break
# If game is not won, tell the players of any full ships they have sunk.
self.sunkShips = self.getSunkShips(grids[1],0)
if len(self.sunkShips) >= 1:
print("Player 1 has sunk...")
for ship in self.sunkShips:
print("Ship of length {}.".format(ship))
else:
print("No ships have yet been sunk.")
input("Press enter for Computer's turn.")
self.clearScreen()
turn = 2
# Computer's turn
if turn == 2:
print("Computer's turn to shoot.")
validShot = False
# Get a possible x and y coordinate to shoot
while validShot == False:
x = -1
y = -1
if compWaitList == [[]]:
while x < 0 or x > 9:
x = random.randint(0,9)
while y < 0 or y > 9:
y = random.randint(0,9)
# Else take the first coord from the waiting list
else:
if compWaitList[0] != []:
x = compWaitList[0][0][0]
y = compWaitList[0][0][1]
compWaitList[0].pop(0)
else:
x = compWaitList[1][0][0]
y = compWaitList[1][0][1]
compWaitList[1].pop(0)
alreadyShot = False
# If proposed x and y coordinate is in shot list then repeat loop
for coord in compShotList:
if coord == [x,y]:
alreadyShot = True
break
if alreadyShot == False:
validShot = True
print("Computer is deciding...")
time.sleep(1)
# Shoot with coords and also add them to used coords
compShotList.append([x,y])
potentialGrid = self.shoot(grids[0],1,[x,y],True)
print("Computer shot at [{},{}].".format(x,y))
# If it was a hit then try adding smart coords to wait list
if potentialGrid[1] == "hit":
print("The computer hit!")
grids[0].show()
# If there has been previous hit of importance and there are still possible hit locations
if compPreviousHits != [] and compWaitList != []:
# If the number of sunk ship increases, get the sunk length then remove the last n-1 possible perpendicular coords
if compSunkShips != self.getSunkShips(grids[0],1):
sunkShipLength = self.getSunkShips(grids[0],1)
print(compSunkShips)
print(sunkShipLength)
for length in compSunkShips:
if sunkShipLength[0] == length:
sunkShipLength.pop(0)
compSunkShips = self.getSunkShips(grids[0],1)
compWaitList[0] = []
# Move the previous hit to last, to be removed
compPreviousHits.append(compPreviousHits[0])
compPreviousHits.pop(0)
compPreviousHits.append([x,y])
del compWaitList[len(compWaitList)-sunkShipLength[0]:]
if compWaitList == []:
compWaitList.append([])
del compPreviousHits[len(compPreviousHits)-sunkShipLength[0]:]
# Else find relation of the two coords
else:
# Set limits to relating to whether they're on the edge and tets relation to last hit
if compPreviousHits[0][0] == x:
# Add next parallel coord (in relation to the hit and previosuly hit coord) to high priority, and perpendicular coords to lowest priority
# This is so there is a higher probability of another hit
if compPreviousHits[0][1] < y:
compWaitList += [[[[x+1,y],[x-1,y]]]]
if y != 9:
compWaitList[0] = [[[x,y+1]]] + compWaitList[0]
else:
compWaitList += [[[[x+1,y],[x-1,y]]]]
if y != 0:
compWaitList[0] = [[x,y-1]] + compWaitList[0]
elif compPreviousHits[0][1] == y:
if compPreviousHits[0][0] < x:
compWaitList += [[[x,y-1],[x,y+1]]]
if x != 9:
compWaitList[0] = [[x+1,y]] + compWaitList[0]
else:
compWaitList += [[[x,y-1],[x,y+1]]]
if x != 0:
compWaitList[0] = [[x-1,y]] + compWaitList[0]
compPreviousHits.append(compPreviousHits[0])
compPreviousHits.pop(0)
compPreviousHits = [[x,y]] + compPreviousHits
else:
# Add adjacent coords to the waiting list, depending on position on the grid
if x == 0:
if y == 0:
compWaitList[0] = [[x+1,y]]
compWaitList.append([[x,y+1]])
elif y == 9:
compWaitList[0] = [[x+1,y]]
compWaitList.append([[x,y-1]])
else:
compWaitList[0] = [[x+1,y]]
compWaitList.append([[x,y-1],[x,y+1]])
elif x == 9:
if y == 0:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x,y+1]])
elif y == 9:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x,y-1]])
else:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x,y-1],[x,y+1]])
elif y == 0:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x+1,y],[x,y+1]])
elif y == 9:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x+1,y],[x,y-1]])
else:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x+1,y],[x,y-1],[x,y+1]])
compPreviousHits.append([x,y])
grids[0].grid = potentialGrid[0]
validMove = True
else:
print("The computer missed!")
grids[0].show()
# Check game is won or not
if grids[0].isDefeated(1) == True:
self.clearScreen()
grids[0].show()
print("Player 2 wins!")
input("Press enter to continue...")
self.clearScreen()
break
self.sunkShips = self.getSunkShips(grids[0],1)
if len(self.sunkShips) >= 1:
print("Computer has sunk...")
for ship in self.sunkShips:
print("Ship of length {}.".format(ship))
else:
print("No ships have yet been sunk.")
input("Press enter for Player 1's turn.")
self.clearScreen()
turn = 1
return
# Function takes in a grid, the opposing side, and the coordinates for the shot
def shoot(self,grid,oSide,shot,isComputer=False):
# Get value in the coord to be shot
coordValue = grid.getCoordValue(shot[0],shot[1])
# If the opponent is the computer
if oSide == 0:
# If value is miss or hit, it is an invalid move
if coordValue == "miss":
print("You've already shot there! Was a miss!")
return "invalid"
elif coordValue == "hit":
print("You've already shot there! Was a hit!")
return "invalid"
# If blank, miss
elif coordValue == "":
print("Miss!")
grid.addPiece([shot],"miss")
return grid.grid
# If computer piece, hit
elif coordValue == 0:
print("Hit!")
grid.addPiece([shot],"hit")
return grid.grid
elif oSide == 1:
if isComputer == True:
if coordValue == "":
grid.addPiece([shot],"miss")
return [grid.grid,"miss"]
elif coordValue == 1:
grid.addPiece([shot],"hit")
return [grid.grid,"hit"]
else:
if coordValue == "miss":
print("You've already shot there! Was a miss!")
return "invalid"
elif coordValue == "hit":
print("You've already shot there! Was a hit!")
return "invalid"
# If shooting at side 2 (own), then it is invalid
elif coordValue == 2:
print("You cannot shoot your own ships!")
return "invalid"
elif coordValue == "":
print("Miss!")
grid.addPiece([shot],"miss")
return grid.grid
# If opponet is 1 and you shoot 1 then it is hit
elif coordValue == 1:
print("Hit!")
grid.addPiece([shot],"hit")
return grid.grid
elif oSide == 2:
if coordValue == "miss":
print("You've already shot there! Was a miss!")
return "invalid"
elif coordValue == "hit":
print("You've already shot there! Was a hit!")
return "invalid"
# If shooting at side 1 (own), then it is invalid
elif coordValue == 1:
print("You cannot shoot your own ships!")
return "invalid"
elif coordValue == "":
print("Miss!")
grid.addPiece([shot],"miss")
return grid.grid
# If opponet is 2 and you shoot 2 then it is hit
elif coordValue == 2:
print("Hit!")
grid.addPiece([shot],"hit")
return grid.grid
# Function takes in a grid, and the number of different ships wanted to add
def makeShips(self,grid,side,shipLengths):
# Add ships of varying lengths
for length in range(len(shipLengths)):
# Adds amount of ships for that length
for amount in range(shipLengths[length]):
ship = self.makeShip(grid,length+2)
grid.addPiece(ship,side)
return grid.grid
# Function returns array of coordinates for a ship
def makeShip(self,grid,length):
ship = []
# Randomise orientation
orientation = random.randint(1,2)
# Whilst valid ship has not been added
while True:
currentShip = []
# Get root position depending on orientation
if orientation == 1:
x = random.randint(0,10-length)
y = random.randint(0,9)
elif orientation == 2:
x = random.randint(0,9)
y = random.randint(0,10-length)
# Add coords depending on length
for i in range(length):
if orientation == 1:
currentShip.append([x+i,y])
elif orientation == 2:
currentShip.append([x,y+i])
# Test that the coords are not filled already
validShip = True
for coord in currentShip:
if grid.isEmpty(coord[0],coord[1]) == False:
# If any coords are filled then the ship is invalid
validShip = False
# If ship is valid then stop trying and return ship coords
if validShip:
keepTrying = False
ship = currentShip
return ship
# Function takes in coordinate inputs
def inputCoord(self,maxX,maxY):
x = -1
y = -1
# While the coordinates are not within grid params
while x < 0 or x > maxX:
try:
x = int(input("Enter X coordinate: "))
except:
x = -1
while y < 0 or y > maxY:
try:
y = int(input("Enter Y coordinate: "))
except:
y = -1
return [x,y]
#Clears the console
def clearScreen(self):
print("\n" * 100)
game = Battleships()
# Battleships 2K16
# Completed 11/11/16, 13:00
# Developed by Tommy Kong
# This is a Battleships game.
# It allows players to:
# Play against each other
# Play against a computer
# Practice shooting ships
import random, time
# Class for creating a grid
class Grid:
# Create gird of width and height
def __init__(self,width,height):
self.grid = []
self.pieces = [[0],[1],[2],["miss"],["hit"]]
for y in range(height):
row = []
for x in range(width):
row.append("")
self.grid.append(row)
# Add piece to given coordinates
def addPiece(self,piece,side):
for pieceSet in self.pieces:
if pieceSet[0] == side:
pieceSet.append(piece)
for coord in piece:
self.grid[coord[1]][coord[0]] = side
# Function checks if the grid still has pieces of a certain side
def isDefeated(self,side):
for row in self.grid:
for piece in row:
if piece == side:
return False
return True
# Display the grid for the user
def show(self):
# Adding the top coordinates
print("-" * 45)
line = ""
for i in range(10):
line += "| " + str(i) + " "
print(line + "| |")
print("-" * 45)
# Adding the individual cells
for y in range(len(self.grid)):
line = ""
for x in self.grid[y]:
if x == "":
line += "| - "
elif x == 0:
line += "| - "
elif x == 1:
line += "| - "
elif x == 2:
line += "| - "
elif x == "miss":
line += "| X "
elif x == "hit":
line += "| O "
# Adding side coordinates
line += "| " + str(y) + " |\n"
for x in self.grid[y]:
line+="----"
line += "-----"
print(line)
# Returns if a grid is empty
def isEmpty(self,x,y):
if self.grid[y][x] == "":
return True
else:
return False
# Returns the value inside a coord
def getCoordValue(self,x,y):
return self.grid[y][x]
# Class which handles the battleships game
class Battleships:
# Allow the user to choose from 3 options
def __init__(self):
while True:
print("Welcome to Battleships!")
print("You can - ")
print("[1] PvP\n[2] PvE\n[3]Just Shoot Ships")
decision = 0
while decision < 1 or decision > 3:
try:
decision = int(input("What would you like to do: "))
except:
decision = 0
if decision == 1:
self.PVP()
elif decision == 2:
self.PVE()
elif decision == 3:
self.JSS()
# Adds a ship
def addShip(self,grid,side,length):
orientation = 0
while orientation < 1 or orientation > 2:
try:
orientation = int(input("Would you like the ship to be horizontal [1] or vertical [2]: "))
except:
orientation = 0
if orientation == 1:
rootCoord = self.inputCoord(10-(length),9)
elif orientation == 2:
rootCoord = self.inputCoord(9,10-(length))
ship = []
# Whilst valid ship has not been added
while True:
currentShip = []
# Add coords depending on length
for i in range(length):
if orientation == 1:
currentShip.append([rootCoord[0]+i,rootCoord[1]])
elif orientation == 2:
currentShip.append([rootCoord[0],rootCoord[1]+i])
# Test that the coords are not filled already
validShip = True
for coord in currentShip:
if grid.isEmpty(coord[0],coord[1]) == False:
# If any coords are filled then the ship is invalid
validShip = False
print("There are already ships existing there!")
return self.addShip(grid,side,length)
# If ship is valid then stop trying and return ship coords
if validShip:
ship = currentShip
return ship
# Function returns list of ship lengths that has been sunk
def getSunkShips(self,grid,side):
# List of sunk ships
sunkShips = []
# Go through the pieces array in grid object
for ship in range(1,len(grid.pieces[side])):
sunkStatus = []
# For each ship coordinate in a ship
for shipCoord in grid.pieces[side][ship]:
# If the coordinate can be found in the hit list then that part has been sunk
sunk = False
for hitCoord in range(1,len(grid.pieces[4])):
if shipCoord == grid.pieces[4][hitCoord][0]:
sunk = True
break
sunkStatus.append(sunk)
# Go through the sunk parts and if all of it is sunk then the ship is sunk
sunkShip = True
for status in sunkStatus:
if status == False:
sunkShip = False
break
if sunkShip == True:
sunkShips.append(ship+1)
return sunkShips
# Method for when the user wants to play against the computer
def PVE(self):
# Create grids
grids = [Grid(10,10),Grid(10,10)]
print("Now you are going to add your ships.")
# Add ships for player 1
print("Player 1 add ships.")
print("You input the root coordinate of the ship. They extend to the right or down, depending on orientation.")
# Add ships of length 2 - 6
for shipLength in range(5):
print("Add ship of length {}".format(shipLength+2))
ship = self.addShip(grids[0],1,shipLength+2)
grids[0].addPiece(ship,1)
input("Press enter to continue...")
self.clearScreen()
print("Okay, the grids are set!")
self.clearScreen()
# Add ships for computer
grids[1].grid = self.makeShips(grids[1],0,[1,1,1,1,1])
turn = 1
# Lists of coords the computer should shoot next
compWaitList = [[]]
# Coords the computer has tried
compShotList = []
compSunkShips = []
compPreviousHits = []
# While there are ships on both side
while grids[0].isDefeated(1) == False and grids[1].isDefeated(0) == False:
# If it is player 1's turn
if turn == 1:
print("Player 1's turn to shoot.")
grids[1].show()
validMove = False
while validMove == False:
# Get shot input and try to shoot
# If shot is not invalid then update the grid
shot = self.inputCoord(9,9)
potentialGrid = self.shoot(grids[1],0,shot)
if potentialGrid != "invalid":
grids[1].grid = potentialGrid
validMove = True
else:
continue
input("Press enter to continue.")
self.clearScreen()
print("Current grid for Player 1.")
grids[1].show()
# Check game is won or not
if grids[1].isDefeated(0) == True:
self.clearScreen()
print("Player 1 wins!")
input("Press enter to continue...")
self.clearScreen()
break
# If game is not won, tell the players of any full ships they have sunk.
self.sunkShips = self.getSunkShips(grids[1],0)
if len(self.sunkShips) >= 1:
print("Player 1 has sunk...")
for ship in self.sunkShips:
print("Ship of length {}.".format(ship))
else:
print("No ships have yet been sunk.")
input("Press enter for Computer's turn.")
self.clearScreen()
turn = 2
# Computer's turn
if turn == 2:
print("Computer's turn to shoot.")
validShot = False
# Get a possible x and y coordinate to shoot
while validShot == False:
x = -1
y = -1
if compWaitList == [[]]:
while x < 0 or x > 9:
x = random.randint(0,9)
while y < 0 or y > 9:
y = random.randint(0,9)
# Else take the first coord from the waiting list
else:
if compWaitList[0] != []:
x = compWaitList[0][0][0]
y = compWaitList[0][0][1]
compWaitList[0].pop(0)
else:
x = compWaitList[1][0][0]
y = compWaitList[1][0][1]
compWaitList[1].pop(0)
alreadyShot = False
# If proposed x and y coordinate is in shot list then repeat loop
for coord in compShotList:
if coord == [x,y]:
alreadyShot = True
break
if alreadyShot == False:
validShot = True
print("Computer is deciding...")
time.sleep(1)
# Shoot with coords and also add them to used coords
compShotList.append([x,y])
potentialGrid = self.shoot(grids[0],1,[x,y],True)
print("Computer shot at [{},{}].".format(x,y))
# If it was a hit then try adding smart coords to wait list
if potentialGrid[1] == "hit":
print("The computer hit!")
grids[0].show()
# If there has been previous hit of importance and there are still possible hit locations
if compPreviousHits != [] and compWaitList != []:
# If the number of sunk ship increases, get the sunk length then remove the last n-1 possible perpendicular coords
if compSunkShips != self.getSunkShips(grids[0],1):
sunkShipLength = self.getSunkShips(grids[0],1)
print(compSunkShips)
print(sunkShipLength)
for length in compSunkShips:
if sunkShipLength[0] == length:
sunkShipLength.pop(0)
compSunkShips = self.getSunkShips(grids[0],1)
compWaitList[0] = []
# Move the previous hit to last, to be removed
compPreviousHits.append(compPreviousHits[0])
compPreviousHits.pop(0)
compPreviousHits.append([x,y])
del compWaitList[len(compWaitList)-sunkShipLength[0]:]
if compWaitList == []:
compWaitList.append([])
del compPreviousHits[len(compPreviousHits)-sunkShipLength[0]:]
# Else find relation of the two coords
else:
# Set limits to relating to whether they're on the edge and tets relation to last hit
if compPreviousHits[0][0] == x:
# Add next parallel coord (in relation to the hit and previosuly hit coord) to high priority, and perpendicular coords to lowest priority
# This is so there is a higher probability of another hit
if compPreviousHits[0][1] < y:
compWaitList += [[[[x+1,y],[x-1,y]]]]
if y != 9:
compWaitList[0] = [[[x,y+1]]] + compWaitList[0]
else:
compWaitList += [[[[x+1,y],[x-1,y]]]]
if y != 0:
compWaitList[0] = [[x,y-1]] + compWaitList[0]
elif compPreviousHits[0][1] == y:
if compPreviousHits[0][0] < x:
compWaitList += [[[x,y-1],[x,y+1]]]
if x != 9:
compWaitList[0] = [[x+1,y]] + compWaitList[0]
else:
compWaitList += [[[x,y-1],[x,y+1]]]
if x != 0:
compWaitList[0] = [[x-1,y]] + compWaitList[0]
compPreviousHits.append(compPreviousHits[0])
compPreviousHits.pop(0)
compPreviousHits = [[x,y]] + compPreviousHits
else:
# Add adjacent coords to the waiting list, depending on position on the grid
if x == 0:
if y == 0:
compWaitList[0] = [[x+1,y]]
compWaitList.append([[x,y+1]])
elif y == 9:
compWaitList[0] = [[x+1,y]]
compWaitList.append([[x,y-1]])
else:
compWaitList[0] = [[x+1,y]]
compWaitList.append([[x,y-1],[x,y+1]])
elif x == 9:
if y == 0:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x,y+1]])
elif y == 9:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x,y-1]])
else:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x,y-1],[x,y+1]])
elif y == 0:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x+1,y],[x,y+1]])
elif y == 9:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x+1,y],[x,y-1]])
else:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x+1,y],[x,y-1],[x,y+1]])
compPreviousHits.append([x,y])
grids[0].grid = potentialGrid[0]
validMove = True
else:
print("The computer missed!")
grids[0].show()
# Check game is won or not
if grids[0].isDefeated(1) == True:
self.clearScreen()
grids[0].show()
print("Player 2 wins!")
input("Press enter to continue...")
self.clearScreen()
break
self.sunkShips = self.getSunkShips(grids[0],1)
if len(self.sunkShips) >= 1:
print("Computer has sunk...")
for ship in self.sunkShips:
print("Ship of length {}.".format(ship))
else:
print("No ships have yet been sunk.")
input("Press enter for Player 1's turn.")
self.clearScreen()
turn = 1
return
# Function takes in a grid, the opposing side, and the coordinates for the shot
def shoot(self,grid,oSide,shot,isComputer=False):
# Get value in the coord to be shot
coordValue = grid.getCoordValue(shot[0],shot[1])
# If the opponent is the computer
if oSide == 0:
# If value is miss or hit, it is an invalid move
if coordValue == "miss":
print("You've already shot there! Was a miss!")
return "invalid"
elif coordValue == "hit":
print("You've already shot there! Was a hit!")
return "invalid"
# If blank, miss
elif coordValue == "":
print("Miss!")
grid.addPiece([shot],"miss")
return grid.grid
# If computer piece, hit
elif coordValue == 0:
print("Hit!")
grid.addPiece([shot],"hit")
return grid.grid
elif oSide == 1:
if isComputer == True:
if coordValue == "":
grid.addPiece([shot],"miss")
return [grid.grid,"miss"]
elif coordValue == 1:
grid.addPiece([shot],"hit")
return [grid.grid,"hit"]
else:
if coordValue == "miss":
print("You've already shot there! Was a miss!")
return "invalid"
elif coordValue == "hit":
print("You've already shot there! Was a hit!")
return "invalid"
# If shooting at side 2 (own), then it is invalid
elif coordValue == 2:
print("You cannot shoot your own ships!")
return "invalid"
elif coordValue == "":
print("Miss!")
grid.addPiece([shot],"miss")
return grid.grid
# If opponet is 1 and you shoot 1 then it is hit
elif coordValue == 1:
print("Hit!")
grid.addPiece([shot],"hit")
return grid.grid
elif oSide == 2:
if coordValue == "miss":
print("You've already shot there! Was a miss!")
return "invalid"
elif coordValue == "hit":
print("You've already shot there! Was a hit!")
return "invalid"
# If shooting at side 1 (own), then it is invalid
elif coordValue == 1:
print("You cannot shoot your own ships!")
return "invalid"
elif coordValue == "":
print("Miss!")
grid.addPiece([shot],"miss")
return grid.grid
# If opponet is 2 and you shoot 2 then it is hit
elif coordValue == 2:
print("Hit!")
grid.addPiece([shot],"hit")
return grid.grid
# Function takes in a grid, and the number of different ships wanted to add
def makeShips(self,grid,side,shipLengths):
# Add ships of varying lengths
for length in range(len(shipLengths)):
# Adds amount of ships for that length
for amount in range(shipLengths[length]):
ship = self.makeShip(grid,length+2)
grid.addPiece(ship,side)
return grid.grid
# Function returns array of coordinates for a ship
def makeShip(self,grid,length):
ship = []
# Randomise orientation
orientation = random.randint(1,2)
# Whilst valid ship has not been added
while True:
currentShip = []
# Get root position depending on orientation
if orientation == 1:
x = random.randint(0,10-length)
y = random.randint(0,9)
elif orientation == 2:
x = random.randint(0,9)
y = random.randint(0,10-length)
# Add coords depending on length
for i in range(length):
if orientation == 1:
currentShip.append([x+i,y])
elif orientation == 2:
currentShip.append([x,y+i])
# Test that the coords are not filled already
validShip = True
for coord in currentShip:
if grid.isEmpty(coord[0],coord[1]) == False:
# If any coords are filled then the ship is invalid
validShip = False
# If ship is valid then stop trying and return ship coords
if validShip:
keepTrying = False
ship = currentShip
return ship
# Function takes in coordinate inputs
def inputCoord(self,maxX,maxY):
x = -1
y = -1
# While the coordinates are not within grid params
while x < 0 or x > maxX:
try:
x = int(input("Enter X coordinate: "))
except:
x = -1
while y < 0 or y > maxY:
try:
y = int(input("Enter Y coordinate: "))
except:
y = -1
return [x,y]
#Clears the console
def clearScreen(self):
print("\n" * 100)
game = Battleships()
# Battleships 2K16
# Completed 11/11/16, 13:00
# Developed by Tommy Kong
# This is a Battleships game.
# It allows players to:
# Play against each other
# Play against a computer
# Practice shooting ships
import random, time
# Class for creating a grid
class Grid:
# Create gird of width and height
def __init__(self,width,height):
self.grid = []
self.pieces = [[0],[1],[2],["miss"],["hit"]]
for y in range(height):
row = []
for x in range(width):
row.append("")
self.grid.append(row)
# Add piece to given coordinates
def addPiece(self,piece,side):
for pieceSet in self.pieces:
if pieceSet[0] == side:
pieceSet.append(piece)
for coord in piece:
self.grid[coord[1]][coord[0]] = side
# Function checks if the grid still has pieces of a certain side
def isDefeated(self,side):
for row in self.grid:
for piece in row:
if piece == side:
return False
return True
# Display the grid for the user
def show(self):
# Adding the top coordinates
print("-" * 45)
line = ""
for i in range(10):
line += "| " + str(i) + " "
print(line + "| |")
print("-" * 45)
# Adding the individual cells
for y in range(len(self.grid)):
line = ""
for x in self.grid[y]:
if x == "":
line += "| - "
elif x == 0:
line += "| - "
elif x == 1:
line += "| - "
elif x == 2:
line += "| - "
elif x == "miss":
line += "| X "
elif x == "hit":
line += "| O "
# Adding side coordinates
line += "| " + str(y) + " |\n"
for x in self.grid[y]:
line+="----"
line += "-----"
print(line)
# Returns if a grid is empty
def isEmpty(self,x,y):
if self.grid[y][x] == "":
return True
else:
return False
# Returns the value inside a coord
def getCoordValue(self,x,y):
return self.grid[y][x]
# Class which handles the battleships game
class Battleships:
# Allow the user to choose from 3 options
def __init__(self):
while True:
print("Welcome to Battleships!")
print("You can - ")
print("[1] PvP\n[2] PvE\n[3]Just Shoot Ships")
decision = 0
while decision < 1 or decision > 3:
try:
decision = int(input("What would you like to do: "))
except:
decision = 0
if decision == 1:
self.PVP()
elif decision == 2:
self.PVE()
elif decision == 3:
self.JSS()
# Adds a ship
def addShip(self,grid,side,length):
orientation = 0
while orientation < 1 or orientation > 2:
try:
orientation = int(input("Would you like the ship to be horizontal [1] or vertical [2]: "))
except:
orientation = 0
if orientation == 1:
rootCoord = self.inputCoord(10-(length),9)
elif orientation == 2:
rootCoord = self.inputCoord(9,10-(length))
ship = []
# Whilst valid ship has not been added
while True:
currentShip = []
# Add coords depending on length
for i in range(length):
if orientation == 1:
currentShip.append([rootCoord[0]+i,rootCoord[1]])
elif orientation == 2:
currentShip.append([rootCoord[0],rootCoord[1]+i])
# Test that the coords are not filled already
validShip = True
for coord in currentShip:
if grid.isEmpty(coord[0],coord[1]) == False:
# If any coords are filled then the ship is invalid
validShip = False
print("There are already ships existing there!")
return self.addShip(grid,side,length)
# If ship is valid then stop trying and return ship coords
if validShip:
ship = currentShip
return ship
# Function returns list of ship lengths that has been sunk
def getSunkShips(self,grid,side):
# List of sunk ships
sunkShips = []
# Go through the pieces array in grid object
for ship in range(1,len(grid.pieces[side])):
sunkStatus = []
# For each ship coordinate in a ship
for shipCoord in grid.pieces[side][ship]:
# If the coordinate can be found in the hit list then that part has been sunk
sunk = False
for hitCoord in range(1,len(grid.pieces[4])):
if shipCoord == grid.pieces[4][hitCoord][0]:
sunk = True
break
sunkStatus.append(sunk)
# Go through the sunk parts and if all of it is sunk then the ship is sunk
sunkShip = True
for status in sunkStatus:
if status == False:
sunkShip = False
break
if sunkShip == True:
sunkShips.append(ship+1)
return sunkShips
# Method for when the user wants to play against the computer
def PVE(self):
# Create grids
grids = [Grid(10,10),Grid(10,10)]
print("Now you are going to add your ships.")
# Add ships for player 1
print("Player 1 add ships.")
print("You input the root coordinate of the ship. They extend to the right or down, depending on orientation.")
# Add ships of length 2 - 6
for shipLength in range(5):
print("Add ship of length {}".format(shipLength+2))
ship = self.addShip(grids[0],1,shipLength+2)
grids[0].addPiece(ship,1)
input("Press enter to continue...")
self.clearScreen()
print("Okay, the grids are set!")
self.clearScreen()
# Add ships for computer
grids[1].grid = self.makeShips(grids[1],0,[1,1,1,1,1])
turn = 1
# Lists of coords the computer should shoot next
compWaitList = [[]]
# Coords the computer has tried
compShotList = []
compSunkShips = []
compPreviousHits = []
# While there are ships on both side
while grids[0].isDefeated(1) == False and grids[1].isDefeated(0) == False:
# If it is player 1's turn
if turn == 1:
print("Player 1's turn to shoot.")
grids[1].show()
validMove = False
while validMove == False:
# Get shot input and try to shoot
# If shot is not invalid then update the grid
shot = self.inputCoord(9,9)
potentialGrid = self.shoot(grids[1],0,shot)
if potentialGrid != "invalid":
grids[1].grid = potentialGrid
validMove = True
else:
continue
input("Press enter to continue.")
self.clearScreen()
print("Current grid for Player 1.")
grids[1].show()
# Check game is won or not
if grids[1].isDefeated(0) == True:
self.clearScreen()
print("Player 1 wins!")
input("Press enter to continue...")
self.clearScreen()
break
# If game is not won, tell the players of any full ships they have sunk.
self.sunkShips = self.getSunkShips(grids[1],0)
if len(self.sunkShips) >= 1:
print("Player 1 has sunk...")
for ship in self.sunkShips:
print("Ship of length {}.".format(ship))
else:
print("No ships have yet been sunk.")
input("Press enter for Computer's turn.")
self.clearScreen()
turn = 2
# Computer's turn
if turn == 2:
print("Computer's turn to shoot.")
validShot = False
# Get a possible x and y coordinate to shoot
while validShot == False:
x = -1
y = -1
if compWaitList == [[]]:
while x < 0 or x > 9:
x = random.randint(0,9)
while y < 0 or y > 9:
y = random.randint(0,9)
# Else take the first coord from the waiting list
else:
if compWaitList[0] != []:
x = compWaitList[0][0][0]
y = compWaitList[0][0][1]
compWaitList[0].pop(0)
else:
x = compWaitList[1][0][0]
y = compWaitList[1][0][1]
compWaitList[1].pop(0)
alreadyShot = False
# If proposed x and y coordinate is in shot list then repeat loop
for coord in compShotList:
if coord == [x,y]:
alreadyShot = True
break
if alreadyShot == False:
validShot = True
print("Computer is deciding...")
time.sleep(1)
# Shoot with coords and also add them to used coords
compShotList.append([x,y])
potentialGrid = self.shoot(grids[0],1,[x,y],True)
print("Computer shot at [{},{}].".format(x,y))
# If it was a hit then try adding smart coords to wait list
if potentialGrid[1] == "hit":
print("The computer hit!")
grids[0].show()
# If there has been previous hit of importance and there are still possible hit locations
if compPreviousHits != [] and compWaitList != []:
# If the number of sunk ship increases, get the sunk length then remove the last n-1 possible perpendicular coords
if compSunkShips != self.getSunkShips(grids[0],1):
sunkShipLength = self.getSunkShips(grids[0],1)
print(compSunkShips)
print(sunkShipLength)
for length in compSunkShips:
if sunkShipLength[0] == length:
sunkShipLength.pop(0)
compSunkShips = self.getSunkShips(grids[0],1)
compWaitList[0] = []
# Move the previous hit to last, to be removed
compPreviousHits.append(compPreviousHits[0])
compPreviousHits.pop(0)
compPreviousHits.append([x,y])
del compWaitList[len(compWaitList)-sunkShipLength[0]:]
if compWaitList == []:
compWaitList.append([])
del compPreviousHits[len(compPreviousHits)-sunkShipLength[0]:]
# Else find relation of the two coords
else:
# Set limits to relating to whether they're on the edge and tets relation to last hit
if compPreviousHits[0][0] == x:
# Add next parallel coord (in relation to the hit and previosuly hit coord) to high priority, and perpendicular coords to lowest priority
# This is so there is a higher probability of another hit
if compPreviousHits[0][1] < y:
compWaitList += [[[[x+1,y],[x-1,y]]]]
if y != 9:
compWaitList[0] = [[[x,y+1]]] + compWaitList[0]
else:
compWaitList += [[[[x+1,y],[x-1,y]]]]
if y != 0:
compWaitList[0] = [[x,y-1]] + compWaitList[0]
elif compPreviousHits[0][1] == y:
if compPreviousHits[0][0] < x:
compWaitList += [[[x,y-1],[x,y+1]]]
if x != 9:
compWaitList[0] = [[x+1,y]] + compWaitList[0]
else:
compWaitList += [[[x,y-1],[x,y+1]]]
if x != 0:
compWaitList[0] = [[x-1,y]] + compWaitList[0]
compPreviousHits.append(compPreviousHits[0])
compPreviousHits.pop(0)
compPreviousHits = [[x,y]] + compPreviousHits
else:
# Add adjacent coords to the waiting list, depending on position on the grid
if x == 0:
if y == 0:
compWaitList[0] = [[x+1,y]]
compWaitList.append([[x,y+1]])
elif y == 9:
compWaitList[0] = [[x+1,y]]
compWaitList.append([[x,y-1]])
else:
compWaitList[0] = [[x+1,y]]
compWaitList.append([[x,y-1],[x,y+1]])
elif x == 9:
if y == 0:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x,y+1]])
elif y == 9:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x,y-1]])
else:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x,y-1],[x,y+1]])
elif y == 0:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x+1,y],[x,y+1]])
elif y == 9:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x+1,y],[x,y-1]])
else:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x+1,y],[x,y-1],[x,y+1]])
compPreviousHits.append([x,y])
grids[0].grid = potentialGrid[0]
validMove = True
else:
print("The computer missed!")
grids[0].show()
# Check game is won or not
if grids[0].isDefeated(1) == True:
self.clearScreen()
grids[0].show()
print("Player 2 wins!")
input("Press enter to continue...")
self.clearScreen()
break
self.sunkShips = self.getSunkShips(grids[0],1)
if len(self.sunkShips) >= 1:
print("Computer has sunk...")
for ship in self.sunkShips:
print("Ship of length {}.".format(ship))
else:
print("No ships have yet been sunk.")
input("Press enter for Player 1's turn.")
self.clearScreen()
turn = 1
return
# Function takes in a grid, the opposing side, and the coordinates for the shot
def shoot(self,grid,oSide,shot,isComputer=False):
# Get value in the coord to be shot
coordValue = grid.getCoordValue(shot[0],shot[1])
# If the opponent is the computer
if oSide == 0:
# If value is miss or hit, it is an invalid move
if coordValue == "miss":
print("You've already shot there! Was a miss!")
return "invalid"
elif coordValue == "hit":
print("You've already shot there! Was a hit!")
return "invalid"
# If blank, miss
elif coordValue == "":
print("Miss!")
grid.addPiece([shot],"miss")
return grid.grid
# If computer piece, hit
elif coordValue == 0:
print("Hit!")
grid.addPiece([shot],"hit")
return grid.grid
elif oSide == 1:
if isComputer == True:
if coordValue == "":
grid.addPiece([shot],"miss")
return [grid.grid,"miss"]
elif coordValue == 1:
grid.addPiece([shot],"hit")
return [grid.grid,"hit"]
else:
if coordValue == "miss":
print("You've already shot there! Was a miss!")
return "invalid"
elif coordValue == "hit":
print("You've already shot there! Was a hit!")
return "invalid"
# If shooting at side 2 (own), then it is invalid
elif coordValue == 2:
print("You cannot shoot your own ships!")
return "invalid"
elif coordValue == "":
print("Miss!")
grid.addPiece([shot],"miss")
return grid.grid
# If opponet is 1 and you shoot 1 then it is hit
elif coordValue == 1:
print("Hit!")
grid.addPiece([shot],"hit")
return grid.grid
elif oSide == 2:
if coordValue == "miss":
print("You've already shot there! Was a miss!")
return "invalid"
elif coordValue == "hit":
print("You've already shot there! Was a hit!")
return "invalid"
# If shooting at side 1 (own), then it is invalid
elif coordValue == 1:
print("You cannot shoot your own ships!")
return "invalid"
elif coordValue == "":
print("Miss!")
grid.addPiece([shot],"miss")
return grid.grid
# If opponet is 2 and you shoot 2 then it is hit
elif coordValue == 2:
print("Hit!")
grid.addPiece([shot],"hit")
return grid.grid
# Function takes in a grid, and the number of different ships wanted to add
def makeShips(self,grid,side,shipLengths):
# Add ships of varying lengths
for length in range(len(shipLengths)):
# Adds amount of ships for that length
for amount in range(shipLengths[length]):
ship = self.makeShip(grid,length+2)
grid.addPiece(ship,side)
return grid.grid
# Function returns array of coordinates for a ship
def makeShip(self,grid,length):
ship = []
# Randomise orientation
orientation = random.randint(1,2)
# Whilst valid ship has not been added
while True:
currentShip = []
# Get root position depending on orientation
if orientation == 1:
x = random.randint(0,10-length)
y = random.randint(0,9)
elif orientation == 2:
x = random.randint(0,9)
y = random.randint(0,10-length)
# Add coords depending on length
for i in range(length):
if orientation == 1:
currentShip.append([x+i,y])
elif orientation == 2:
currentShip.append([x,y+i])
# Test that the coords are not filled already
validShip = True
for coord in currentShip:
if grid.isEmpty(coord[0],coord[1]) == False:
# If any coords are filled then the ship is invalid
validShip = False
# If ship is valid then stop trying and return ship coords
if validShip:
keepTrying = False
ship = currentShip
return ship
# Function takes in coordinate inputs
def inputCoord(self,maxX,maxY):
x = -1
y = -1
# While the coordinates are not within grid params
while x < 0 or x > maxX:
try:
x = int(input("Enter X coordinate: "))
except:
x = -1
while y < 0 or y > maxY:
try:
y = int(input("Enter Y coordinate: "))
except:
y = -1
return [x,y]
#Clears the console
def clearScreen(self):
print("\n" * 100)
game = Battleships()
Thank you for reading. I am a Year 12 student studying CS for my A-Levels. I have had previous Python experience but it is obviously not to professional/industry standard. Therefore, my goal for this review is for my code to be criticised, allowing me to improve towards reaching such standards.
Here is what I think I'd like a review on-:
# Battleships 2K16
# Completed 11/11/16, 13:00
# Developed by Tommy Kong
# This is a Battleships game.
# It allows players to:
# Play against each other
# Play against a computer
# Practice shooting ships
import random, time
Class for creating a grid
class Grid:
# CreateBattleships gird2K16
# ofCompleted width11/11/16, and13:00
# heightDeveloped by Tommy Kong
def# __init__(self,width,height):
This is a Battleships selfgame.grid = []
self.pieces = [[0],[1],[2],["miss"],["hit"]]
# forIt yallows inplayers range(height)to:
row = []
# forPlay xagainst ineach range(width):other
# Play against a row.append("")computer
# Practice shooting self.grid.append(row)ships
# Add piece to given coordinates
def addPiece(self,piece,side):
for pieceSet in self.pieces:
if pieceSet[0] == side:
pieceSet.append(piece)
for coord in piece:
self.grid[coord[1]][coord[0]] = side
# Function checks if the grid still has pieces of a certain side
defimport isDefeated(selfrandom,side):
for row in self.grid:
for piece in row:
if piece == side:
return False
return Truetime
# Display the grid for the user
def# show(self):Class for creating a grid
# Adding the top coordinates
print("-" * 45)
line = ""
for i inclass range(10)Grid:
line += "| " + str(i) + " "
print(line + "| |")
print("-" * 45)
# AddingCreate thegird individualof cellswidth and height
for y indef range(len__init__(self.grid),width,height):
lineself.grid = ""[]
for x in self.grid[y]:
if x == "":
line += "| - "
elif x == 0:
line += "| - "
elif x == 1:
line += "|pieces -= "[[0],[1],[2],["miss"],["hit"]]
eliffor xy ==in 2range(height):
line += "|row -= "[]
eliffor x ==in "miss"range(width):
line += "| X "
elif x == "hit":row.append("")
line += "| O "self.grid.append(row)
# Add piece to #given Addingcoordinates
def addPiece(self,piece,side):
coordinates for pieceSet in self.pieces:
if pieceSet[0] == side:
pieceSet.append(piece)
for coord in piece:
self.grid[coord[1]][coord[0]] = side
# Function checks if linethe +=grid "|still "has +pieces str(y)of +a "certain |\n"side
def isDefeated(self,side):
for xrow in self.grid[y]grid:
line+="----"for piece in row:
line += "-----"
if piece print(line)
== side:
# Returns if a grid is empty
def isEmpty(self,x,y):
if self.grid[y][x] == "": return False
return True
else:
return False
# Returns the value inside a coord
def# getCoordValue(self,x,y):
Display the grid for returnthe self.grid[y][x]user
Class which handles the battleships game
class Battleships:
# Allow the user to
choose from 3 optionsdef show(self):
def __init__(self): # Adding the top coordinates
while True:
print("Welcome"-" to* Battleships!"45)
printline = ""
for i in range("You10):
can - line += "| " + str(i) + " "
print("[1]line PvP\n[2]+ PvE\n[3]Just"| Shoot Ships" |")
decisionprint("-" =* 045)
while# decisionAdding <the 1individual orcells
decision > 3
for y in range(len(self.grid)):
tryline = ""
for x in self.grid[y]:
decisionif =x int(input("What== would"":
you like to do line += "| - "
elif x == 0:
line += "| - "))
except elif x == 1:
decision = 0 line += "| - "
elif x == 2:
line += "| - "
elif x == "miss":
line += "| X "
elif x == "hit":
line += "| O "
if decision == 1:
self.PVP()
elif# decisionAdding ==side 2:coordinates
self.PVE()
elif decision == 3:
line += "| " + self.JSSstr(y)
# Adds+ a" ship|\n"
def addShip(self,grid,side,length):
orientation = 0
while orientation < 1 orfor orientationx >in 2self.grid[y]:
try:
orientationline+="----"
= int(input("Would you like the ship to be horizontal [1] or verticalline [2]:+= "))
except:-----"
orientation = 0print(line)
if orientation == 1:
rootCoord = self.inputCoord(10-(length),9)
elif orientation == 2:
# Returns if a rootCoordgrid =is self.inputCoord(9,10-(length))empty
shipdef =isEmpty(self,x,y):
[] if self.grid[y][x] == "":
return True
else:
return False
# Whilst validReturns shipthe hasvalue notinside beena addedcoord
while True:
currentShip = []
# Add coords depending on length
for i in range(length):
if orientation == 1:
currentShip.append([rootCoord[0]+i,rootCoord[1]])
elif orientation == 2:
def currentShip.appendgetCoordValue([rootCoord[0]self,rootCoord[1]+i])
# Test that the coords are not filled already
validShip = True
for coord in currentShip:
if grid.isEmpty(coord[0]x,coord[1]y) == False:
# If any coords are filled then the ship is invalid
validShip = False
print("There are already ships existing there!")
return self.addShip(grid,side,length)
# If ship is valid then stop trying and return ship coords
if validShip:
ship = currentShip
return shipgrid[y][x]
# Function returns list of shipClass lengthswhich thathandles hasthe beenbattleships sunkgame
defclass getSunkShips(self,grid,side)Battleships:
# ListAllow ofthe sunkuser shipsto choose from 3 options
sunkShipsdef __init__(self):
while True:
print("Welcome to Battleships!")
print("You can - ")
print("[1] PvP\n[2] PvE\n[3]Just Shoot Ships")
decision = []0
# Go through the pieces array in grid objectwhile decision < 1 or decision > 3:
for ship in range try:
decision = int(1,leninput(grid.pieces[side]"What would you like to do: "))
except:
sunkStatus decision = []0
# For each ship coordinateif indecision a== ship1:
for shipCoord in gridself.pieces[side][ship]:
PVP()
# If the coordinate can be found in the hit list then that part has been sunk
elif decision == 2:
sunk = False
for hitCoord in range(1,len(gridself.pieces[4])PVE():
ifelif shipCoorddecision == grid.pieces[4][hitCoord][0]3:
sunk = Trueself.JSS()
break
# Adds a sunkStatus.append(sunk)ship
# Go through the sunk parts and if all of it is sunk then the ship isdef sunkaddShip(self,grid,side,length):
sunkShiporientation = True0
forwhile statusorientation in< sunkStatus:
1 or orientation > 2:
if status == False try:
sunkShiporientation = Falseint(input("Would you like the ship to be horizontal [1] or vertical [2]: "))
except:
break orientation = 0
if sunkShiporientation == 1:
rootCoord = self.inputCoord(10-(length),9)
elif orientation == 2:
rootCoord = self.inputCoord(9,10-(length))
ship = []
# Whilst valid ship has not been added
while True:
sunkShipscurrentShip = []
# Add coords depending on length
for i in range(length):
if orientation == 1:
currentShip.append(ship+1[rootCoord[0]+i,rootCoord[1]])
return sunkShips elif orientation == 2:
currentShip.append([rootCoord[0],rootCoord[1]+i])
# MethodTest that the coords are not filled already
validShip = True
for whencoord thein usercurrentShip:
wants to play against if grid.isEmpty(coord[0],coord[1]) == False:
# If any coords are filled then the computership is invalid
validShip = False
print("There are already ships existing there!")
return self.addShip(grid,side,length)
def PVE(self) # If ship is valid then stop trying and return ship coords
if validShip:
ship = currentShip
return ship
# CreateFunction gridsreturns list of ship lengths that has been sunk
grids =def [GridgetSunkShips(10,10)self,Grid(10grid,10side)]:
print("Now you are going to# addList yourof sunk ships.")
sunkShips = []
# Add ships for player# 1
Go through the pieces
array in grid object
print("Player 1 add ships.")
print("Youfor inputship thein rootrange(1,len(grid.pieces[side])):
coordinate of the ship. They extend to the right or down, dependingsunkStatus on= orientation.")[]
# AddFor shipseach ofship lengthcoordinate 2in -a 6ship
for shipLengthshipCoord in range(5)grid.pieces[side][ship]:
print("Add ship of length {}".format(shipLength+2))
ship # If the coordinate can be found in the hit list then that part has been sunk
sunk = self.addShip(grids[0],1,shipLength+2)False
grids[0].addPiece for hitCoord in range(ship,1,len(grid.pieces[4])):
input("Press enter to continue.. if shipCoord == grid.")pieces[4][hitCoord][0]:
self.clearScreen() sunk = True
print("Okay, the grids are set!") break
self sunkStatus.clearScreenappend(sunk)
# AddGo shipsthrough the sunk parts and if all of it is sunk then the ship is sunk
sunkShip = True
for computerstatus in sunkStatus:
if status == False:
grids[1].grid sunkShip = self.makeShips(grids[1],0,[1,1,1,1,1])False
break
if sunkShip == True:
turn = 1 sunkShips.append(ship+1)
return sunkShips
# Method for when the user wants to play against the computer
# Lists of coords the computer should shoot next
compWaitList =def [[]]PVE(self):
# Coords the computer has# triedCreate grids
compShotList = []
compSunkShips = []
compPreviousHitsgrids = [][Grid(10,10),Grid(10,10)]
# While there are ships on both side
print("Now you whileare grids[0].isDefeated(1)going ==to Falseadd andyour grids[1]ships.isDefeated(0") == False:
# IfAdd itships isfor player 1's turn1
if turn == 1:
print("Player 1's turn1 toadd shootships.")
grids[1].showprint()
validMove = False
"You input
the root coordinate of the ship. They extend to the right or whiledown, validMovedepending ==on False:orientation.")
# Add ships of length 2 - 6
# Get shot input and try to shoot
for shipLength in range(5):
# If shot is not invalid then updateprint("Add theship gridof length {}".format(shipLength+2))
ship = self.addShip(grids[0],1,shipLength+2)
grids[0].addPiece(ship,1)
input("Press enter to continue...")
shot = self.inputCoordclearScreen(9,9)
print("Okay, the grids are set!")
potentialGrid = self.shootclearScreen(grids[1],0,shot)
# ifAdd potentialGridships !=for "invalid":computer
grids[1].grid = potentialGrid
validMove = True
else:
continue
input("Press enter to continue.")
self.clearScreen()
printmakeShips("Current grid for Player 1.")
grids[1].show(,0,[1,1,1,1,1])
# Check game is won or not
if grids[1].isDefeated(0) == True:
self.clearScreen()
turn print("Player= 1 wins!")
input("Press enter to continue...")
self.clearScreen()
break
# Lists of coords #the Ifcomputer gameshould isshoot notnext
won, tell the players of any full ships
they have sunk. compWaitList = [[]]
self.sunkShips = self.getSunkShips(grids[1],0)
if len(self.sunkShips) >= 1:
# Coords print("Playerthe 1computer has sunk...")
for ship in self.sunkShips:
print("Ship of length {}.".format(ship))
else:
print("No ships have yet been sunk.")
tried
input("Press enter forcompShotList Computer's= turn.")[]
compSunkShips = self.clearScreen()[]
turncompPreviousHits = 2[]
# Computer'sWhile turnthere are ships on both side
ifwhile turngrids[0].isDefeated(1) == 2:
print("Computer's turnFalse toand shootgrids[1]."isDefeated(0)
validShot === False:
# Get a possible xIf andit yis coordinateplayer to1's shootturn
whileif validShotturn == False1:
xprint("Player =1's -1turn to shoot.")
ygrids[1].show()
validMove = -1False
if compWaitList == [[]]:
while x < 0 or xvalidMove >== 9False:
x = random.randint(0,9)
while# yGet <shot 0input orand ytry >to 9:shoot
# If shot is ynot invalid then update the grid
shot = randomself.randintinputCoord(09,9)
potentialGrid = self.shoot(grids[1],0,shot)
# Else take the first coord from the waiting list
else:
if compWaitList[0]potentialGrid != []"invalid":
xgrids[1].grid = compWaitList[0][0][0]potentialGrid
yvalidMove = compWaitList[0][0][1]
compWaitList[0].pop(0)True
else:
xcontinue
= compWaitList[1][0][0]
input("Press enter to continue.")
y = compWaitList[1][0][1] self.clearScreen()
print("Current grid for Player 1.")
compWaitList[1] grids[1].popshow(0)
alreadyShot# =Check Falsegame is won or not
if grids[1].isDefeated(0) == True:
self.clearScreen()
print("Player 1 wins!")
input("Press enter to continue...")
self.clearScreen()
break
# If proposedgame xis andnot ywon, coordinatetell isthe inplayers shotof listany thenfull repeatships loopthey have sunk.
forself.sunkShips coord= inself.getSunkShips(grids[1],0)
compShotList if len(self.sunkShips) >= 1:
ifprint("Player coord1 ==has [x,y]sunk...")
for ship in self.sunkShips:
alreadyShotprint("Ship =of Truelength {}.".format(ship))
else:
break print("No ships have yet been sunk.")
ifinput("Press alreadyShotenter ==for False:Computer's turn.")
self.clearScreen()
validShot turn = True2
print("Computer is deciding...")
time.sleep(1)
# Shoot with coords and also add them to usedComputer's coordsturn
compShotList.append([x,y])if turn == 2:
potentialGrid = self.shoot(grids[0],1,[x,y],True)
print("Computer"Computer's shotturn atto [{},{}]shoot.".format(x,y))
validShot = False
# If it was a# hitGet thena trypossible addingx smartand coordsy coordinate to waitshoot
list
while validShot == False:
x = -1
y = -1
if potentialGrid[1]compWaitList == "hit"[[]]:
print("The computer hit!") while x < 0 or x > 9:
grids[0] x = random.showrandint(0,9)
# If there has been previous hit of importancewhile andy there< are0 stillor possibley hit> locations9:
if compPreviousHits != [] and compWaitList ! y = []:random.randint(0,9)
# If the number of sunk ship increases,Else gettake the sunk lengthfirst thencoord removefrom the last n-1 possible perpendicularwaiting coordslist
else:
if compSunkShipscompWaitList[0] != self.getSunkShips(grids[0],1)[]:
sunkShipLength x = self.getSunkShips(grids[0],1)compWaitList[0][0][0]
print(compSunkShips) y = compWaitList[0][0][1]
print compWaitList[0].pop(sunkShipLength0)
forelse:
length in compSunkShips:
x = compWaitList[1][0][0]
if sunkShipLength[0] == length:
y = compWaitList[1][0][1]
sunkShipLength compWaitList[1].pop(0)
compSunkShips = self.getSunkShips(grids[0],1)
compWaitList[0]alreadyShot = []False
# If proposed x #and Movey thecoordinate previousis hitin toshot last,list tothen berepeat removedloop
for coord in compShotList:
compPreviousHits.append(compPreviousHits[0])if coord == [x,y]:
compPreviousHits.pop(0) alreadyShot = True
compPreviousHits.append([x,y]) break
if alreadyShot == False:
del compWaitList[len(compWaitList)-sunkShipLength[0]:] validShot = True
print("Computer is deciding...")
if compWaitList == []: time.sleep(1)
compWaitList # Shoot with coords and also add them to used coords
compShotList.append([][x,y])
potentialGrid = self.shoot(grids[0],1,[x,y],True)
del compPreviousHits[len print(compPreviousHits)-sunkShipLength[0]:"Computer shot at [{},{}].".format(x,y))
# If it #was Elsea findhit relationthen oftry theadding twosmart coords to wait list
else if potentialGrid[1] == "hit":
print("The computer hit!")
grids[0].show()
# If there has been previous hit of importance and there are still possible hit locations
if compPreviousHits != [] and compWaitList != []:
# SetIf limitsthe tonumber relatingof tosunk whethership they'reincreases, onget the edgesunk andlength tetsthen relationremove tothe last hitn-1 possible perpendicular coords
if compPreviousHits[0][0]compSunkShips != self.getSunkShips(grids[0],1):
sunkShipLength = self.getSunkShips(grids[0],1)
print(compSunkShips)
print(sunkShipLength)
for length in compSunkShips:
if sunkShipLength[0] == xlength:
sunkShipLength.pop(0)
#compSunkShips Add= nextself.getSunkShips(grids[0],1)
parallel coord (in relation to the hit and previosuly hit coord) to high priority, and perpendicular coords to lowest priority
compWaitList[0] = []
# This is so there is a higher probability of another# Move the previous hit to last, to be removed
if compPreviousHits[0][1] < y:
compWaitList += [[[[x+1,y],[x-1,y]]]]
if y != 9:
compWaitList[0] = [[[x,y+1]]] + compWaitList[0]
else:
compWaitList += [[[[x+1,y],[x-1,y]]]]compPreviousHits.append(compPreviousHits[0])
if y != compPreviousHits.pop(0:
compWaitList[0] = [[x,y-1]] + compWaitList[0]
elif compPreviousHits[0][1] == y:
if compPreviousHits[0][0] < x:)
compWaitList += [[[x,y-1],compPreviousHits.append([x,y+1]]]
if x != 9:
compWaitList[0] = [[x+1,y]] + compWaitList[0]y])
else:
del compWaitList[len(compWaitList += [[[x,y)-1],[x,y+1]]]
if x != 0sunkShipLength[0]:
compWaitList[0] = [[x-1,y]] + compWaitList[0]]
compPreviousHits.append(compPreviousHits[0])
compPreviousHits.pop(0)
compPreviousHits = [[x,y]] + compPreviousHits
else:
# Add adjacent coords to the waiting list, depending on position on the grid
if x == 0:
if ycompWaitList == 0[]:
compWaitList[0] = [[x+1,y]]
compWaitList.append([[x,y+1]][])
elif y == 9:
del compPreviousHits[len(compPreviousHits)-sunkShipLength[0]:]
compWaitList[0] = [[x+1,y]]
# Else find relation of the two compWaitList.append([[x,y-1]])coords
else:
# Set limits to relating to whether they're on the edge and tets relation to last hit
if compPreviousHits[0][0] == x:
# Add next parallel coord (in relation to the hit and previosuly hit coord) to high priority, and perpendicular coords to lowest priority
# This is so there is a higher probability of another hit
if compPreviousHits[0][1] < y:
compWaitList += [[[[x+1,y],[x-1,y]]]]
if y != 9:
compWaitList[0] = [[[x,y+1]]] + compWaitList[0]
else:
compWaitList += [[[[x+1,y],[x-1,y]]]]
if y != 0:
compWaitList[0] = [[x,y-1]] + compWaitList[0]
elif compPreviousHits[0][1] == y:
if compPreviousHits[0][0] < x:
compWaitList += [[[x,y-1],[x,y+1]]]
if x != 9:
compWaitList[0] = [[x+1,y]] + compWaitList[0]
else:
compWaitList += [[[x,y-1],[x,y+1]]]
if x != 0:
compWaitList[0] = [[x-1,y]] + compWaitList[0]
compPreviousHits.append(compPreviousHits[0])
compPreviousHits.pop(0)
compPreviousHits = [[x,y]] + compPreviousHits
else:
# Add adjacent coords to the waiting list, depending on position on the grid
if x == 0:
if y == 0:
compWaitList[0] = [[x+1,y]]
compWaitList.append([[x,y+1]])
elif y == 9:
compWaitList[0] = [[x+1,y]]
compWaitList.append([[x,y-1]])
else:
compWaitList[0] = [[x+1,y]]
compWaitList.append([[x,y-1],[x,y+1]])
elif x == 9:
if y == 0:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x,y+1]])
elif y == 9:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x,y-1]])
else:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x,y-1],[x,y+1]])
elif y == 0:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x[[x+1,y],[x,y+1]])
elif y == 9:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x[[x+1,y],[x,y-1]])
else:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x,y-1],[x,y+1]])
elif y == 0:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x+1,y],[x,y+1]])
elif y == 9:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x+1,y],[x,y-1]])
else:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x+1,y],[x,y-1],[x,y+1]])
compPreviousHits.append([x,y])
grids[0].grid = potentialGrid[0]
validMove = True
else:
print("The computer missed!")
grids[0].show()
# Check game is won or not
if grids[0].grid = potentialGrid[0]
validMoveisDefeated(1) === True
else:
print("The computer missed!")
grids[0]self.showclearScreen()
# Check game is won or not
if grids[0].isDefeated(1) == True:
self.clearScreenshow()
grids[0].show()
print("Player 2 wins!")
input("Press enter to continue...")
self.clearScreen()
break
self.sunkShips = self.getSunkShips(grids[0],1)
if len(self.sunkShips) >= 1:
print("Computer has sunk...")
for ship in self.sunkShips:
print("Ship of length {}.".formatclearScreen(ship))
else:
print("No ships have yet been sunk.")break
input("Press enter for Player 1'sself.sunkShips turn= self."getSunkShips(grids[0],1)
if len(self.clearScreensunkShips) >= 1:
print("Computer has sunk...")
turn = 1 for ship in self.sunkShips:
print("Ship of length {}.".format(ship))
else:
return print("No ships have yet been sunk.")
# Function takes in a grid, the opposing side, and the coordinates input("Press enter for thePlayer shot
1's turn.")
def shoot( self,grid,oSide,shot,isComputer=False.clearScreen():
turn = 1
# Get value in the coord to be shot
coordValue = grid.getCoordValue(shot[0],shot[1]) return
# IfFunction takes in a grid, the opponentopposing isside, and the computercoordinates for the shot
if oSide == 0:
# If value is miss or hit, it is an invalid move
if coordValue == "miss":
print("You've already shot there! Was a miss!")
return "invalid"
elif coordValue == "hit":
def printshoot("You've already shot there! Was a hit!")
return "invalid"
# If blankself, miss
elif coordValue == "":
print("Miss!")
grid.addPiece([shot],"miss"oSide,shot,isComputer=False)
return grid.grid:
# If computer piece, hit
Get value in the coord to be shot
elif coordValue == 0:
print("Hit!")
= grid.addPiecegetCoordValue([shot]shot[0],"hit"shot[1])
# If the opponent is returnthe grid.gridcomputer
elif if oSide == 10:
# If value is miss or hit, it is an invalid move
if isComputercoordValue == True"miss":
if print("You've already shot there! Was a miss!")
return "invalid"
elif coordValue == "hit":
print("You've already shot there! Was a hit!")
return "invalid"
# If blank, miss
elif coordValue == "":
print("Miss!")
grid.addPiece([shot],"miss")
return [gridgrid.grid
# If computer piece,"miss"] hit
elif coordValue == 10:
print("Hit!")
grid.addPiece([shot],"hit")
return grid.grid
elif oSide == 1:
if isComputer == True:
if coordValue == "":
grid.addPiece([shot],"miss")
return [grid.grid,"miss"]
elif coordValue == 1:
grid.addPiece([shot],"hit")
return [grid.grid,"hit"]
else:
if coordValue == "miss":
print("You've already shot there! Was a miss!")
return "invalid"
elif coordValue == "hit":
print("You've already shot there! Was a hit!")
return "invalid"
# If shooting at side 2 (own), then it is invalid
elif coordValue == 2:
print("You cannot shoot your own ships!")
return "invalid"
elif coordValue == "":
print("Miss!")
grid.addPiece([shot],"miss")
return grid.grid
# If opponet is 1 and you shoot 1 then it is hit
elif coordValue == 1:
print("Hit!")
grid.addPiece([shot],"hit")
return grid.grid
elif oSide == 2:
if coordValue == "miss":
print("You've already shot there! Was a miss!")
return "invalid"
elif coordValue == "hit":
print("You've already shot there! Was a hit!")
return "invalid"
# If shooting at side 21 (own), then it is invalid
elif coordValue == 21:
print("You cannot shoot your own ships!")
return "invalid"
elif coordValue == "":
print("Miss!")
grid.addPiece([shot],"miss")
return grid.grid
# If opponet is 12 and you shoot 12 then it is hit
elif coordValue == 12:
print("Hit!")
grid.addPiece([shot],"hit")
return grid.grid
elif oSide == 2:
if coordValue == "miss":
print("You've already shot there! Was a miss!")
return "invalid"
elif coordValue == "hit":
print("You've already shot there! Was a hit!")
return "invalid"
# If shooting at side 1 (own), then it is invalid
elif coordValue == 1:
print("You cannot shoot your own ships!")
return "invalid"
elif coordValue == "":
print("Miss!")
# Function takes in a grid.addPiece([shot],"miss")
return grid.grid
# If opponet is 2 and you shoot 2 then it is hit
the number of different ships wanted to add
elif coordValue == 2:
def printmakeShips("Hit!")
self,grid.addPiece([shot],"hit"side,shipLengths)
return grid.grid:
# Add ships of varying lengths
# Function takes in a grid, and the number of different ships wantedfor tolength add
defin makeShipsrange(self,grid,side,len(shipLengths)):
# AddAdds shipsamount of varyingships lengthsfor that length
for amount in range(shipLengths[length]):
ship = self.makeShip(grid,length+2)
grid.addPiece(ship,side)
return grid.grid
for length in range(len(shipLengths)):
# Adds amount of ships for that length
for amount in range(shipLengths[length]):
ship = self.makeShip(grid,length+2)
grid.addPiece(ship,side)
return grid.grid
# Function returns array of coordinates for a ship
def makeShip(self,grid,length):
ship = []
# Randomise orientation
orientation =def random.randintmakeShip(1self,2grid,length):
ship = []
# Whilst valid ship has not# beenRandomise addedorientation
while True:
currentShiporientation = []random.randint(1,2)
# GetWhilst rootvalid positionship dependinghas onnot orientationbeen added
while True:
currentShip = []
if orientation == 1:
x = random.randint(0,10-length)
y = random.randint(0,9)
# Get root position depending elifon orientation == 2:
x = random.randint(0,9)
y = random.randint(0,10-length)
# Add coords depending on length
for i in range(length):
if orientation == 1:
currentShipx = random.appendrandint([x+i0,y]10-length)
y = random.randint(0,9)
elif orientation == 2:
currentShipx = random.appendrandint([x0,y+i]9)
# Test that the coords are not filled already
validShipy = True
for coord in currentShip:
if gridrandom.isEmptyrandint(coord[0]0,coord[1]10-length) == False:
# If any coords are filled then the ship is invalid
validShip = False
# If ship is valid then stop trying and return ship coords
if validShip:
keepTrying = False
ship = currentShip
return ship
# Function takes in coordinate inputs
def inputCoord # Add coords depending on length
for i in range(self,maxXlength):
if orientation == 1:
currentShip.append([x+i,maxYy])
elif orientation == 2:
x currentShip.append([x,y+i])
# Test that the coords are not filled already
validShip = -1True
y for coord in currentShip:
if grid.isEmpty(coord[0],coord[1]) == False:
# If any coords are filled then the ship is invalid
validShip = -1False
# WhileIf theship coordinatesis arevalid notthen withinstop gridtrying paramsand return ship coords
if validShip:
keepTrying = False
ship = currentShip
return ship
while x <# 0Function ortakes xin >coordinate maxX:inputs
try:
x =def int(inputinputCoord("Enter X coordinate: ")self,maxX,maxY):
except:
x = -1
xy = -1
# While the coordinates are not within grid params
while yx < 0 or yx > maxYmaxX:
try:
y x = int(input("Enter YX coordinate: "))
except:
y x = -1
return [x,y] while y < 0 or y > maxY:
try:
y = int(input("Enter Y coordinate: "))
except:
y = -1
#Clears the console return [x,y]
def clearScreen(self):
print("\n"#Clears *the 100)console
game = Battleships def clearScreen(self):
print("\n" * 100)
game = Battleships()
Thank you for reading. I am a Year 12 student studying CS for my A-Levels. I have had previous Python experience but it is obviously not to professional/industry standard. Therefore, my goal for this review is for my code to be criticised, allowing me to improve towards reaching such standards.
Here is what I think I'd like a review on-
# Battleships 2K16
# Completed 11/11/16, 13:00
# Developed by Tommy Kong
# This is a Battleships game.
# It allows players to:
# Play against each other
# Play against a computer
# Practice shooting ships
import random, time
Class for creating a grid
class Grid:
# Create gird of width and height
def __init__(self,width,height):
self.grid = []
self.pieces = [[0],[1],[2],["miss"],["hit"]]
for y in range(height):
row = []
for x in range(width):
row.append("")
self.grid.append(row)
# Add piece to given coordinates
def addPiece(self,piece,side):
for pieceSet in self.pieces:
if pieceSet[0] == side:
pieceSet.append(piece)
for coord in piece:
self.grid[coord[1]][coord[0]] = side
# Function checks if the grid still has pieces of a certain side
def isDefeated(self,side):
for row in self.grid:
for piece in row:
if piece == side:
return False
return True
# Display the grid for the user
def show(self):
# Adding the top coordinates
print("-" * 45)
line = ""
for i in range(10):
line += "| " + str(i) + " "
print(line + "| |")
print("-" * 45)
# Adding the individual cells
for y in range(len(self.grid)):
line = ""
for x in self.grid[y]:
if x == "":
line += "| - "
elif x == 0:
line += "| - "
elif x == 1:
line += "| - "
elif x == 2:
line += "| - "
elif x == "miss":
line += "| X "
elif x == "hit":
line += "| O "
# Adding side coordinates
line += "| " + str(y) + " |\n"
for x in self.grid[y]:
line+="----"
line += "-----"
print(line)
# Returns if a grid is empty
def isEmpty(self,x,y):
if self.grid[y][x] == "":
return True
else:
return False
# Returns the value inside a coord
def getCoordValue(self,x,y):
return self.grid[y][x]
Class which handles the battleships game
class Battleships:
# Allow the user to choose from 3 options
def __init__(self):
while True:
print("Welcome to Battleships!")
print("You can - ")
print("[1] PvP\n[2] PvE\n[3]Just Shoot Ships")
decision = 0
while decision < 1 or decision > 3:
try:
decision = int(input("What would you like to do: "))
except:
decision = 0
if decision == 1:
self.PVP()
elif decision == 2:
self.PVE()
elif decision == 3:
self.JSS()
# Adds a ship
def addShip(self,grid,side,length):
orientation = 0
while orientation < 1 or orientation > 2:
try:
orientation = int(input("Would you like the ship to be horizontal [1] or vertical [2]: "))
except:
orientation = 0
if orientation == 1:
rootCoord = self.inputCoord(10-(length),9)
elif orientation == 2:
rootCoord = self.inputCoord(9,10-(length))
ship = []
# Whilst valid ship has not been added
while True:
currentShip = []
# Add coords depending on length
for i in range(length):
if orientation == 1:
currentShip.append([rootCoord[0]+i,rootCoord[1]])
elif orientation == 2:
currentShip.append([rootCoord[0],rootCoord[1]+i])
# Test that the coords are not filled already
validShip = True
for coord in currentShip:
if grid.isEmpty(coord[0],coord[1]) == False:
# If any coords are filled then the ship is invalid
validShip = False
print("There are already ships existing there!")
return self.addShip(grid,side,length)
# If ship is valid then stop trying and return ship coords
if validShip:
ship = currentShip
return ship
# Function returns list of ship lengths that has been sunk
def getSunkShips(self,grid,side):
# List of sunk ships
sunkShips = []
# Go through the pieces array in grid object
for ship in range(1,len(grid.pieces[side])):
sunkStatus = []
# For each ship coordinate in a ship
for shipCoord in grid.pieces[side][ship]:
# If the coordinate can be found in the hit list then that part has been sunk
sunk = False
for hitCoord in range(1,len(grid.pieces[4])):
if shipCoord == grid.pieces[4][hitCoord][0]:
sunk = True
break
sunkStatus.append(sunk)
# Go through the sunk parts and if all of it is sunk then the ship is sunk
sunkShip = True
for status in sunkStatus:
if status == False:
sunkShip = False
break
if sunkShip == True:
sunkShips.append(ship+1)
return sunkShips
# Method for when the user wants to play against the computer
def PVE(self):
# Create grids
grids = [Grid(10,10),Grid(10,10)]
print("Now you are going to add your ships.")
# Add ships for player 1
print("Player 1 add ships.")
print("You input the root coordinate of the ship. They extend to the right or down, depending on orientation.")
# Add ships of length 2 - 6
for shipLength in range(5):
print("Add ship of length {}".format(shipLength+2))
ship = self.addShip(grids[0],1,shipLength+2)
grids[0].addPiece(ship,1)
input("Press enter to continue...")
self.clearScreen()
print("Okay, the grids are set!")
self.clearScreen()
# Add ships for computer
grids[1].grid = self.makeShips(grids[1],0,[1,1,1,1,1])
turn = 1
# Lists of coords the computer should shoot next
compWaitList = [[]]
# Coords the computer has tried
compShotList = []
compSunkShips = []
compPreviousHits = []
# While there are ships on both side
while grids[0].isDefeated(1) == False and grids[1].isDefeated(0) == False:
# If it is player 1's turn
if turn == 1:
print("Player 1's turn to shoot.")
grids[1].show()
validMove = False
while validMove == False:
# Get shot input and try to shoot
# If shot is not invalid then update the grid
shot = self.inputCoord(9,9)
potentialGrid = self.shoot(grids[1],0,shot)
if potentialGrid != "invalid":
grids[1].grid = potentialGrid
validMove = True
else:
continue
input("Press enter to continue.")
self.clearScreen()
print("Current grid for Player 1.")
grids[1].show()
# Check game is won or not
if grids[1].isDefeated(0) == True:
self.clearScreen()
print("Player 1 wins!")
input("Press enter to continue...")
self.clearScreen()
break
# If game is not won, tell the players of any full ships they have sunk.
self.sunkShips = self.getSunkShips(grids[1],0)
if len(self.sunkShips) >= 1:
print("Player 1 has sunk...")
for ship in self.sunkShips:
print("Ship of length {}.".format(ship))
else:
print("No ships have yet been sunk.")
input("Press enter for Computer's turn.")
self.clearScreen()
turn = 2
# Computer's turn
if turn == 2:
print("Computer's turn to shoot.")
validShot = False
# Get a possible x and y coordinate to shoot
while validShot == False:
x = -1
y = -1
if compWaitList == [[]]:
while x < 0 or x > 9:
x = random.randint(0,9)
while y < 0 or y > 9:
y = random.randint(0,9)
# Else take the first coord from the waiting list
else:
if compWaitList[0] != []:
x = compWaitList[0][0][0]
y = compWaitList[0][0][1]
compWaitList[0].pop(0)
else:
x = compWaitList[1][0][0]
y = compWaitList[1][0][1]
compWaitList[1].pop(0)
alreadyShot = False
# If proposed x and y coordinate is in shot list then repeat loop
for coord in compShotList:
if coord == [x,y]:
alreadyShot = True
break
if alreadyShot == False:
validShot = True
print("Computer is deciding...")
time.sleep(1)
# Shoot with coords and also add them to used coords
compShotList.append([x,y])
potentialGrid = self.shoot(grids[0],1,[x,y],True)
print("Computer shot at [{},{}].".format(x,y))
# If it was a hit then try adding smart coords to wait list
if potentialGrid[1] == "hit":
print("The computer hit!")
grids[0].show()
# If there has been previous hit of importance and there are still possible hit locations
if compPreviousHits != [] and compWaitList != []:
# If the number of sunk ship increases, get the sunk length then remove the last n-1 possible perpendicular coords
if compSunkShips != self.getSunkShips(grids[0],1):
sunkShipLength = self.getSunkShips(grids[0],1)
print(compSunkShips)
print(sunkShipLength)
for length in compSunkShips:
if sunkShipLength[0] == length:
sunkShipLength.pop(0)
compSunkShips = self.getSunkShips(grids[0],1)
compWaitList[0] = []
# Move the previous hit to last, to be removed
compPreviousHits.append(compPreviousHits[0])
compPreviousHits.pop(0)
compPreviousHits.append([x,y])
del compWaitList[len(compWaitList)-sunkShipLength[0]:]
if compWaitList == []:
compWaitList.append([])
del compPreviousHits[len(compPreviousHits)-sunkShipLength[0]:]
# Else find relation of the two coords
else:
# Set limits to relating to whether they're on the edge and tets relation to last hit
if compPreviousHits[0][0] == x:
# Add next parallel coord (in relation to the hit and previosuly hit coord) to high priority, and perpendicular coords to lowest priority
# This is so there is a higher probability of another hit
if compPreviousHits[0][1] < y:
compWaitList += [[[[x+1,y],[x-1,y]]]]
if y != 9:
compWaitList[0] = [[[x,y+1]]] + compWaitList[0]
else:
compWaitList += [[[[x+1,y],[x-1,y]]]]
if y != 0:
compWaitList[0] = [[x,y-1]] + compWaitList[0]
elif compPreviousHits[0][1] == y:
if compPreviousHits[0][0] < x:
compWaitList += [[[x,y-1],[x,y+1]]]
if x != 9:
compWaitList[0] = [[x+1,y]] + compWaitList[0]
else:
compWaitList += [[[x,y-1],[x,y+1]]]
if x != 0:
compWaitList[0] = [[x-1,y]] + compWaitList[0]
compPreviousHits.append(compPreviousHits[0])
compPreviousHits.pop(0)
compPreviousHits = [[x,y]] + compPreviousHits
else:
# Add adjacent coords to the waiting list, depending on position on the grid
if x == 0:
if y == 0:
compWaitList[0] = [[x+1,y]]
compWaitList.append([[x,y+1]])
elif y == 9:
compWaitList[0] = [[x+1,y]]
compWaitList.append([[x,y-1]])
else:
compWaitList[0] = [[x+1,y]]
compWaitList.append([[x,y-1],[x,y+1]])
elif x == 9:
if y == 0:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x,y+1]])
elif y == 9:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x,y-1]])
else:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x,y-1],[x,y+1]])
elif y == 0:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x+1,y],[x,y+1]])
elif y == 9:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x+1,y],[x,y-1]])
else:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x+1,y],[x,y-1],[x,y+1]])
compPreviousHits.append([x,y])
grids[0].grid = potentialGrid[0]
validMove = True
else:
print("The computer missed!")
grids[0].show()
# Check game is won or not
if grids[0].isDefeated(1) == True:
self.clearScreen()
grids[0].show()
print("Player 2 wins!")
input("Press enter to continue...")
self.clearScreen()
break
self.sunkShips = self.getSunkShips(grids[0],1)
if len(self.sunkShips) >= 1:
print("Computer has sunk...")
for ship in self.sunkShips:
print("Ship of length {}.".format(ship))
else:
print("No ships have yet been sunk.")
input("Press enter for Player 1's turn.")
self.clearScreen()
turn = 1
return
# Function takes in a grid, the opposing side, and the coordinates for the shot
def shoot(self,grid,oSide,shot,isComputer=False):
# Get value in the coord to be shot
coordValue = grid.getCoordValue(shot[0],shot[1])
# If the opponent is the computer
if oSide == 0:
# If value is miss or hit, it is an invalid move
if coordValue == "miss":
print("You've already shot there! Was a miss!")
return "invalid"
elif coordValue == "hit":
print("You've already shot there! Was a hit!")
return "invalid"
# If blank, miss
elif coordValue == "":
print("Miss!")
grid.addPiece([shot],"miss")
return grid.grid
# If computer piece, hit
elif coordValue == 0:
print("Hit!")
grid.addPiece([shot],"hit")
return grid.grid
elif oSide == 1:
if isComputer == True:
if coordValue == "":
grid.addPiece([shot],"miss")
return [grid.grid,"miss"]
elif coordValue == 1:
grid.addPiece([shot],"hit")
return [grid.grid,"hit"]
else:
if coordValue == "miss":
print("You've already shot there! Was a miss!")
return "invalid"
elif coordValue == "hit":
print("You've already shot there! Was a hit!")
return "invalid"
# If shooting at side 2 (own), then it is invalid
elif coordValue == 2:
print("You cannot shoot your own ships!")
return "invalid"
elif coordValue == "":
print("Miss!")
grid.addPiece([shot],"miss")
return grid.grid
# If opponet is 1 and you shoot 1 then it is hit
elif coordValue == 1:
print("Hit!")
grid.addPiece([shot],"hit")
return grid.grid
elif oSide == 2:
if coordValue == "miss":
print("You've already shot there! Was a miss!")
return "invalid"
elif coordValue == "hit":
print("You've already shot there! Was a hit!")
return "invalid"
# If shooting at side 1 (own), then it is invalid
elif coordValue == 1:
print("You cannot shoot your own ships!")
return "invalid"
elif coordValue == "":
print("Miss!")
grid.addPiece([shot],"miss")
return grid.grid
# If opponet is 2 and you shoot 2 then it is hit
elif coordValue == 2:
print("Hit!")
grid.addPiece([shot],"hit")
return grid.grid
# Function takes in a grid, and the number of different ships wanted to add
def makeShips(self,grid,side,shipLengths):
# Add ships of varying lengths
for length in range(len(shipLengths)):
# Adds amount of ships for that length
for amount in range(shipLengths[length]):
ship = self.makeShip(grid,length+2)
grid.addPiece(ship,side)
return grid.grid
# Function returns array of coordinates for a ship
def makeShip(self,grid,length):
ship = []
# Randomise orientation
orientation = random.randint(1,2)
# Whilst valid ship has not been added
while True:
currentShip = []
# Get root position depending on orientation
if orientation == 1:
x = random.randint(0,10-length)
y = random.randint(0,9)
elif orientation == 2:
x = random.randint(0,9)
y = random.randint(0,10-length)
# Add coords depending on length
for i in range(length):
if orientation == 1:
currentShip.append([x+i,y])
elif orientation == 2:
currentShip.append([x,y+i])
# Test that the coords are not filled already
validShip = True
for coord in currentShip:
if grid.isEmpty(coord[0],coord[1]) == False:
# If any coords are filled then the ship is invalid
validShip = False
# If ship is valid then stop trying and return ship coords
if validShip:
keepTrying = False
ship = currentShip
return ship
# Function takes in coordinate inputs
def inputCoord(self,maxX,maxY):
x = -1
y = -1
# While the coordinates are not within grid params
while x < 0 or x > maxX:
try:
x = int(input("Enter X coordinate: "))
except:
x = -1
while y < 0 or y > maxY:
try:
y = int(input("Enter Y coordinate: "))
except:
y = -1
return [x,y]
#Clears the console
def clearScreen(self):
print("\n" * 100)
game = Battleships()
I am a Year 12 student studying CS for my A-Levels. I have had previous Python experience but it is obviously not to professional/industry standard. Therefore, my goal for this review is for my code to be criticised, allowing me to improve towards reaching such standards.
Here is what I think I'd like a review on:
# Battleships 2K16
# Completed 11/11/16, 13:00
# Developed by Tommy Kong
# This is a Battleships game.
# It allows players to:
# Play against each other
# Play against a computer
# Practice shooting ships
import random, time
# Class for creating a grid
class Grid:
# Create gird of width and height
def __init__(self,width,height):
self.grid = []
self.pieces = [[0],[1],[2],["miss"],["hit"]]
for y in range(height):
row = []
for x in range(width):
row.append("")
self.grid.append(row)
# Add piece to given coordinates
def addPiece(self,piece,side):
for pieceSet in self.pieces:
if pieceSet[0] == side:
pieceSet.append(piece)
for coord in piece:
self.grid[coord[1]][coord[0]] = side
# Function checks if the grid still has pieces of a certain side
def isDefeated(self,side):
for row in self.grid:
for piece in row:
if piece == side:
return False
return True
# Display the grid for the user
def show(self):
# Adding the top coordinates
print("-" * 45)
line = ""
for i in range(10):
line += "| " + str(i) + " "
print(line + "| |")
print("-" * 45)
# Adding the individual cells
for y in range(len(self.grid)):
line = ""
for x in self.grid[y]:
if x == "":
line += "| - "
elif x == 0:
line += "| - "
elif x == 1:
line += "| - "
elif x == 2:
line += "| - "
elif x == "miss":
line += "| X "
elif x == "hit":
line += "| O "
# Adding side coordinates
line += "| " + str(y) + " |\n"
for x in self.grid[y]:
line+="----"
line += "-----"
print(line)
# Returns if a grid is empty
def isEmpty(self,x,y):
if self.grid[y][x] == "":
return True
else:
return False
# Returns the value inside a coord
def getCoordValue(self,x,y):
return self.grid[y][x]
# Class which handles the battleships game
class Battleships:
# Allow the user to choose from 3 options
def __init__(self):
while True:
print("Welcome to Battleships!")
print("You can - ")
print("[1] PvP\n[2] PvE\n[3]Just Shoot Ships")
decision = 0
while decision < 1 or decision > 3:
try:
decision = int(input("What would you like to do: "))
except:
decision = 0
if decision == 1:
self.PVP()
elif decision == 2:
self.PVE()
elif decision == 3:
self.JSS()
# Adds a ship
def addShip(self,grid,side,length):
orientation = 0
while orientation < 1 or orientation > 2:
try:
orientation = int(input("Would you like the ship to be horizontal [1] or vertical [2]: "))
except:
orientation = 0
if orientation == 1:
rootCoord = self.inputCoord(10-(length),9)
elif orientation == 2:
rootCoord = self.inputCoord(9,10-(length))
ship = []
# Whilst valid ship has not been added
while True:
currentShip = []
# Add coords depending on length
for i in range(length):
if orientation == 1:
currentShip.append([rootCoord[0]+i,rootCoord[1]])
elif orientation == 2:
currentShip.append([rootCoord[0],rootCoord[1]+i])
# Test that the coords are not filled already
validShip = True
for coord in currentShip:
if grid.isEmpty(coord[0],coord[1]) == False:
# If any coords are filled then the ship is invalid
validShip = False
print("There are already ships existing there!")
return self.addShip(grid,side,length)
# If ship is valid then stop trying and return ship coords
if validShip:
ship = currentShip
return ship
# Function returns list of ship lengths that has been sunk
def getSunkShips(self,grid,side):
# List of sunk ships
sunkShips = []
# Go through the pieces array in grid object
for ship in range(1,len(grid.pieces[side])):
sunkStatus = []
# For each ship coordinate in a ship
for shipCoord in grid.pieces[side][ship]:
# If the coordinate can be found in the hit list then that part has been sunk
sunk = False
for hitCoord in range(1,len(grid.pieces[4])):
if shipCoord == grid.pieces[4][hitCoord][0]:
sunk = True
break
sunkStatus.append(sunk)
# Go through the sunk parts and if all of it is sunk then the ship is sunk
sunkShip = True
for status in sunkStatus:
if status == False:
sunkShip = False
break
if sunkShip == True:
sunkShips.append(ship+1)
return sunkShips
# Method for when the user wants to play against the computer
def PVE(self):
# Create grids
grids = [Grid(10,10),Grid(10,10)]
print("Now you are going to add your ships.")
# Add ships for player 1
print("Player 1 add ships.")
print("You input the root coordinate of the ship. They extend to the right or down, depending on orientation.")
# Add ships of length 2 - 6
for shipLength in range(5):
print("Add ship of length {}".format(shipLength+2))
ship = self.addShip(grids[0],1,shipLength+2)
grids[0].addPiece(ship,1)
input("Press enter to continue...")
self.clearScreen()
print("Okay, the grids are set!")
self.clearScreen()
# Add ships for computer
grids[1].grid = self.makeShips(grids[1],0,[1,1,1,1,1])
turn = 1
# Lists of coords the computer should shoot next
compWaitList = [[]]
# Coords the computer has tried
compShotList = []
compSunkShips = []
compPreviousHits = []
# While there are ships on both side
while grids[0].isDefeated(1) == False and grids[1].isDefeated(0) == False:
# If it is player 1's turn
if turn == 1:
print("Player 1's turn to shoot.")
grids[1].show()
validMove = False
while validMove == False:
# Get shot input and try to shoot
# If shot is not invalid then update the grid
shot = self.inputCoord(9,9)
potentialGrid = self.shoot(grids[1],0,shot)
if potentialGrid != "invalid":
grids[1].grid = potentialGrid
validMove = True
else:
continue
input("Press enter to continue.")
self.clearScreen()
print("Current grid for Player 1.")
grids[1].show()
# Check game is won or not
if grids[1].isDefeated(0) == True:
self.clearScreen()
print("Player 1 wins!")
input("Press enter to continue...")
self.clearScreen()
break
# If game is not won, tell the players of any full ships they have sunk.
self.sunkShips = self.getSunkShips(grids[1],0)
if len(self.sunkShips) >= 1:
print("Player 1 has sunk...")
for ship in self.sunkShips:
print("Ship of length {}.".format(ship))
else:
print("No ships have yet been sunk.")
input("Press enter for Computer's turn.")
self.clearScreen()
turn = 2
# Computer's turn
if turn == 2:
print("Computer's turn to shoot.")
validShot = False
# Get a possible x and y coordinate to shoot
while validShot == False:
x = -1
y = -1
if compWaitList == [[]]:
while x < 0 or x > 9:
x = random.randint(0,9)
while y < 0 or y > 9:
y = random.randint(0,9)
# Else take the first coord from the waiting list
else:
if compWaitList[0] != []:
x = compWaitList[0][0][0]
y = compWaitList[0][0][1]
compWaitList[0].pop(0)
else:
x = compWaitList[1][0][0]
y = compWaitList[1][0][1]
compWaitList[1].pop(0)
alreadyShot = False
# If proposed x and y coordinate is in shot list then repeat loop
for coord in compShotList:
if coord == [x,y]:
alreadyShot = True
break
if alreadyShot == False:
validShot = True
print("Computer is deciding...")
time.sleep(1)
# Shoot with coords and also add them to used coords
compShotList.append([x,y])
potentialGrid = self.shoot(grids[0],1,[x,y],True)
print("Computer shot at [{},{}].".format(x,y))
# If it was a hit then try adding smart coords to wait list
if potentialGrid[1] == "hit":
print("The computer hit!")
grids[0].show()
# If there has been previous hit of importance and there are still possible hit locations
if compPreviousHits != [] and compWaitList != []:
# If the number of sunk ship increases, get the sunk length then remove the last n-1 possible perpendicular coords
if compSunkShips != self.getSunkShips(grids[0],1):
sunkShipLength = self.getSunkShips(grids[0],1)
print(compSunkShips)
print(sunkShipLength)
for length in compSunkShips:
if sunkShipLength[0] == length:
sunkShipLength.pop(0)
compSunkShips = self.getSunkShips(grids[0],1)
compWaitList[0] = []
# Move the previous hit to last, to be removed
compPreviousHits.append(compPreviousHits[0])
compPreviousHits.pop(0)
compPreviousHits.append([x,y])
del compWaitList[len(compWaitList)-sunkShipLength[0]:]
if compWaitList == []:
compWaitList.append([])
del compPreviousHits[len(compPreviousHits)-sunkShipLength[0]:]
# Else find relation of the two coords
else:
# Set limits to relating to whether they're on the edge and tets relation to last hit
if compPreviousHits[0][0] == x:
# Add next parallel coord (in relation to the hit and previosuly hit coord) to high priority, and perpendicular coords to lowest priority
# This is so there is a higher probability of another hit
if compPreviousHits[0][1] < y:
compWaitList += [[[[x+1,y],[x-1,y]]]]
if y != 9:
compWaitList[0] = [[[x,y+1]]] + compWaitList[0]
else:
compWaitList += [[[[x+1,y],[x-1,y]]]]
if y != 0:
compWaitList[0] = [[x,y-1]] + compWaitList[0]
elif compPreviousHits[0][1] == y:
if compPreviousHits[0][0] < x:
compWaitList += [[[x,y-1],[x,y+1]]]
if x != 9:
compWaitList[0] = [[x+1,y]] + compWaitList[0]
else:
compWaitList += [[[x,y-1],[x,y+1]]]
if x != 0:
compWaitList[0] = [[x-1,y]] + compWaitList[0]
compPreviousHits.append(compPreviousHits[0])
compPreviousHits.pop(0)
compPreviousHits = [[x,y]] + compPreviousHits
else:
# Add adjacent coords to the waiting list, depending on position on the grid
if x == 0:
if y == 0:
compWaitList[0] = [[x+1,y]]
compWaitList.append([[x,y+1]])
elif y == 9:
compWaitList[0] = [[x+1,y]]
compWaitList.append([[x,y-1]])
else:
compWaitList[0] = [[x+1,y]]
compWaitList.append([[x,y-1],[x,y+1]])
elif x == 9:
if y == 0:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x,y+1]])
elif y == 9:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x,y-1]])
else:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x,y-1],[x,y+1]])
elif y == 0:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x+1,y],[x,y+1]])
elif y == 9:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x+1,y],[x,y-1]])
else:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x+1,y],[x,y-1],[x,y+1]])
compPreviousHits.append([x,y])
grids[0].grid = potentialGrid[0]
validMove = True
else:
print("The computer missed!")
grids[0].show()
# Check game is won or not
if grids[0].isDefeated(1) == True:
self.clearScreen()
grids[0].show()
print("Player 2 wins!")
input("Press enter to continue...")
self.clearScreen()
break
self.sunkShips = self.getSunkShips(grids[0],1)
if len(self.sunkShips) >= 1:
print("Computer has sunk...")
for ship in self.sunkShips:
print("Ship of length {}.".format(ship))
else:
print("No ships have yet been sunk.")
input("Press enter for Player 1's turn.")
self.clearScreen()
turn = 1
return
# Function takes in a grid, the opposing side, and the coordinates for the shot
def shoot(self,grid,oSide,shot,isComputer=False):
# Get value in the coord to be shot
coordValue = grid.getCoordValue(shot[0],shot[1])
# If the opponent is the computer
if oSide == 0:
# If value is miss or hit, it is an invalid move
if coordValue == "miss":
print("You've already shot there! Was a miss!")
return "invalid"
elif coordValue == "hit":
print("You've already shot there! Was a hit!")
return "invalid"
# If blank, miss
elif coordValue == "":
print("Miss!")
grid.addPiece([shot],"miss")
return grid.grid
# If computer piece, hit
elif coordValue == 0:
print("Hit!")
grid.addPiece([shot],"hit")
return grid.grid
elif oSide == 1:
if isComputer == True:
if coordValue == "":
grid.addPiece([shot],"miss")
return [grid.grid,"miss"]
elif coordValue == 1:
grid.addPiece([shot],"hit")
return [grid.grid,"hit"]
else:
if coordValue == "miss":
print("You've already shot there! Was a miss!")
return "invalid"
elif coordValue == "hit":
print("You've already shot there! Was a hit!")
return "invalid"
# If shooting at side 2 (own), then it is invalid
elif coordValue == 2:
print("You cannot shoot your own ships!")
return "invalid"
elif coordValue == "":
print("Miss!")
grid.addPiece([shot],"miss")
return grid.grid
# If opponet is 1 and you shoot 1 then it is hit
elif coordValue == 1:
print("Hit!")
grid.addPiece([shot],"hit")
return grid.grid
elif oSide == 2:
if coordValue == "miss":
print("You've already shot there! Was a miss!")
return "invalid"
elif coordValue == "hit":
print("You've already shot there! Was a hit!")
return "invalid"
# If shooting at side 1 (own), then it is invalid
elif coordValue == 1:
print("You cannot shoot your own ships!")
return "invalid"
elif coordValue == "":
print("Miss!")
grid.addPiece([shot],"miss")
return grid.grid
# If opponet is 2 and you shoot 2 then it is hit
elif coordValue == 2:
print("Hit!")
grid.addPiece([shot],"hit")
return grid.grid
# Function takes in a grid, and the number of different ships wanted to add
def makeShips(self,grid,side,shipLengths):
# Add ships of varying lengths
for length in range(len(shipLengths)):
# Adds amount of ships for that length
for amount in range(shipLengths[length]):
ship = self.makeShip(grid,length+2)
grid.addPiece(ship,side)
return grid.grid
# Function returns array of coordinates for a ship
def makeShip(self,grid,length):
ship = []
# Randomise orientation
orientation = random.randint(1,2)
# Whilst valid ship has not been added
while True:
currentShip = []
# Get root position depending on orientation
if orientation == 1:
x = random.randint(0,10-length)
y = random.randint(0,9)
elif orientation == 2:
x = random.randint(0,9)
y = random.randint(0,10-length)
# Add coords depending on length
for i in range(length):
if orientation == 1:
currentShip.append([x+i,y])
elif orientation == 2:
currentShip.append([x,y+i])
# Test that the coords are not filled already
validShip = True
for coord in currentShip:
if grid.isEmpty(coord[0],coord[1]) == False:
# If any coords are filled then the ship is invalid
validShip = False
# If ship is valid then stop trying and return ship coords
if validShip:
keepTrying = False
ship = currentShip
return ship
# Function takes in coordinate inputs
def inputCoord(self,maxX,maxY):
x = -1
y = -1
# While the coordinates are not within grid params
while x < 0 or x > maxX:
try:
x = int(input("Enter X coordinate: "))
except:
x = -1
while y < 0 or y > maxY:
try:
y = int(input("Enter Y coordinate: "))
except:
y = -1
return [x,y]
#Clears the console
def clearScreen(self):
print("\n" * 100)
game = Battleships()
Battleships Player vs Computer Algorithm
Thank you for reading. I am a Year 12 student studying CS for my A-Levels. I have had previous Python experience but it is obviously not to professional/industry standard. Therefore, my goal for this review is for my code to be criticised, allowing me to improve towards reaching such standards.
The program is a computer version of the Battleships game. I have taken out the PvP and "Just Shoot Ships" methods. I believe the most interesting part is the PVE() function. This is whether the logic is implemented for the computer to play against the player.
Here is what I think I'd like a review on-
- The use of variables and their names
- The use of classes - how I've passed objects into functions; I think my use of classes and passing in objects is fairly inconsistent. I would like to know if there is a way I could improve this.
- The use of list and whether it is good practice to use jagged arrays in such a way.
- The efficiency of my algorithm in general if possible.
- The effectiveness of commenting and how it can be improved to be more useful in a team environment
# Battleships 2K16
# Completed 11/11/16, 13:00
# Developed by Tommy Kong
# This is a Battleships game.
# It allows players to:
# Play against each other
# Play against a computer
# Practice shooting ships
import random, time
Class for creating a grid
class Grid:
# Create gird of width and height
def __init__(self,width,height):
self.grid = []
self.pieces = [[0],[1],[2],["miss"],["hit"]]
for y in range(height):
row = []
for x in range(width):
row.append("")
self.grid.append(row)
# Add piece to given coordinates
def addPiece(self,piece,side):
for pieceSet in self.pieces:
if pieceSet[0] == side:
pieceSet.append(piece)
for coord in piece:
self.grid[coord[1]][coord[0]] = side
# Function checks if the grid still has pieces of a certain side
def isDefeated(self,side):
for row in self.grid:
for piece in row:
if piece == side:
return False
return True
# Display the grid for the user
def show(self):
# Adding the top coordinates
print("-" * 45)
line = ""
for i in range(10):
line += "| " + str(i) + " "
print(line + "| |")
print("-" * 45)
# Adding the individual cells
for y in range(len(self.grid)):
line = ""
for x in self.grid[y]:
if x == "":
line += "| - "
elif x == 0:
line += "| - "
elif x == 1:
line += "| - "
elif x == 2:
line += "| - "
elif x == "miss":
line += "| X "
elif x == "hit":
line += "| O "
# Adding side coordinates
line += "| " + str(y) + " |\n"
for x in self.grid[y]:
line+="----"
line += "-----"
print(line)
# Returns if a grid is empty
def isEmpty(self,x,y):
if self.grid[y][x] == "":
return True
else:
return False
# Returns the value inside a coord
def getCoordValue(self,x,y):
return self.grid[y][x]
Class which handles the battleships game
class Battleships:
# Allow the user to choose from 3 options
def __init__(self):
while True:
print("Welcome to Battleships!")
print("You can - ")
print("[1] PvP\n[2] PvE\n[3]Just Shoot Ships")
decision = 0
while decision < 1 or decision > 3:
try:
decision = int(input("What would you like to do: "))
except:
decision = 0
if decision == 1:
self.PVP()
elif decision == 2:
self.PVE()
elif decision == 3:
self.JSS()
# Adds a ship
def addShip(self,grid,side,length):
orientation = 0
while orientation < 1 or orientation > 2:
try:
orientation = int(input("Would you like the ship to be horizontal [1] or vertical [2]: "))
except:
orientation = 0
if orientation == 1:
rootCoord = self.inputCoord(10-(length),9)
elif orientation == 2:
rootCoord = self.inputCoord(9,10-(length))
ship = []
# Whilst valid ship has not been added
while True:
currentShip = []
# Add coords depending on length
for i in range(length):
if orientation == 1:
currentShip.append([rootCoord[0]+i,rootCoord[1]])
elif orientation == 2:
currentShip.append([rootCoord[0],rootCoord[1]+i])
# Test that the coords are not filled already
validShip = True
for coord in currentShip:
if grid.isEmpty(coord[0],coord[1]) == False:
# If any coords are filled then the ship is invalid
validShip = False
print("There are already ships existing there!")
return self.addShip(grid,side,length)
# If ship is valid then stop trying and return ship coords
if validShip:
ship = currentShip
return ship
# Function returns list of ship lengths that has been sunk
def getSunkShips(self,grid,side):
# List of sunk ships
sunkShips = []
# Go through the pieces array in grid object
for ship in range(1,len(grid.pieces[side])):
sunkStatus = []
# For each ship coordinate in a ship
for shipCoord in grid.pieces[side][ship]:
# If the coordinate can be found in the hit list then that part has been sunk
sunk = False
for hitCoord in range(1,len(grid.pieces[4])):
if shipCoord == grid.pieces[4][hitCoord][0]:
sunk = True
break
sunkStatus.append(sunk)
# Go through the sunk parts and if all of it is sunk then the ship is sunk
sunkShip = True
for status in sunkStatus:
if status == False:
sunkShip = False
break
if sunkShip == True:
sunkShips.append(ship+1)
return sunkShips
# Method for when the user wants to play against the computer
def PVE(self):
# Create grids
grids = [Grid(10,10),Grid(10,10)]
print("Now you are going to add your ships.")
# Add ships for player 1
print("Player 1 add ships.")
print("You input the root coordinate of the ship. They extend to the right or down, depending on orientation.")
# Add ships of length 2 - 6
for shipLength in range(5):
print("Add ship of length {}".format(shipLength+2))
ship = self.addShip(grids[0],1,shipLength+2)
grids[0].addPiece(ship,1)
input("Press enter to continue...")
self.clearScreen()
print("Okay, the grids are set!")
self.clearScreen()
# Add ships for computer
grids[1].grid = self.makeShips(grids[1],0,[1,1,1,1,1])
turn = 1
# Lists of coords the computer should shoot next
compWaitList = [[]]
# Coords the computer has tried
compShotList = []
compSunkShips = []
compPreviousHits = []
# While there are ships on both side
while grids[0].isDefeated(1) == False and grids[1].isDefeated(0) == False:
# If it is player 1's turn
if turn == 1:
print("Player 1's turn to shoot.")
grids[1].show()
validMove = False
while validMove == False:
# Get shot input and try to shoot
# If shot is not invalid then update the grid
shot = self.inputCoord(9,9)
potentialGrid = self.shoot(grids[1],0,shot)
if potentialGrid != "invalid":
grids[1].grid = potentialGrid
validMove = True
else:
continue
input("Press enter to continue.")
self.clearScreen()
print("Current grid for Player 1.")
grids[1].show()
# Check game is won or not
if grids[1].isDefeated(0) == True:
self.clearScreen()
print("Player 1 wins!")
input("Press enter to continue...")
self.clearScreen()
break
# If game is not won, tell the players of any full ships they have sunk.
self.sunkShips = self.getSunkShips(grids[1],0)
if len(self.sunkShips) >= 1:
print("Player 1 has sunk...")
for ship in self.sunkShips:
print("Ship of length {}.".format(ship))
else:
print("No ships have yet been sunk.")
input("Press enter for Computer's turn.")
self.clearScreen()
turn = 2
# Computer's turn
if turn == 2:
print("Computer's turn to shoot.")
validShot = False
# Get a possible x and y coordinate to shoot
while validShot == False:
x = -1
y = -1
if compWaitList == [[]]:
while x < 0 or x > 9:
x = random.randint(0,9)
while y < 0 or y > 9:
y = random.randint(0,9)
# Else take the first coord from the waiting list
else:
if compWaitList[0] != []:
x = compWaitList[0][0][0]
y = compWaitList[0][0][1]
compWaitList[0].pop(0)
else:
x = compWaitList[1][0][0]
y = compWaitList[1][0][1]
compWaitList[1].pop(0)
alreadyShot = False
# If proposed x and y coordinate is in shot list then repeat loop
for coord in compShotList:
if coord == [x,y]:
alreadyShot = True
break
if alreadyShot == False:
validShot = True
print("Computer is deciding...")
time.sleep(1)
# Shoot with coords and also add them to used coords
compShotList.append([x,y])
potentialGrid = self.shoot(grids[0],1,[x,y],True)
print("Computer shot at [{},{}].".format(x,y))
# If it was a hit then try adding smart coords to wait list
if potentialGrid[1] == "hit":
print("The computer hit!")
grids[0].show()
# If there has been previous hit of importance and there are still possible hit locations
if compPreviousHits != [] and compWaitList != []:
# If the number of sunk ship increases, get the sunk length then remove the last n-1 possible perpendicular coords
if compSunkShips != self.getSunkShips(grids[0],1):
sunkShipLength = self.getSunkShips(grids[0],1)
print(compSunkShips)
print(sunkShipLength)
for length in compSunkShips:
if sunkShipLength[0] == length:
sunkShipLength.pop(0)
compSunkShips = self.getSunkShips(grids[0],1)
compWaitList[0] = []
# Move the previous hit to last, to be removed
compPreviousHits.append(compPreviousHits[0])
compPreviousHits.pop(0)
compPreviousHits.append([x,y])
del compWaitList[len(compWaitList)-sunkShipLength[0]:]
if compWaitList == []:
compWaitList.append([])
del compPreviousHits[len(compPreviousHits)-sunkShipLength[0]:]
# Else find relation of the two coords
else:
# Set limits to relating to whether they're on the edge and tets relation to last hit
if compPreviousHits[0][0] == x:
# Add next parallel coord (in relation to the hit and previosuly hit coord) to high priority, and perpendicular coords to lowest priority
# This is so there is a higher probability of another hit
if compPreviousHits[0][1] < y:
compWaitList += [[[[x+1,y],[x-1,y]]]]
if y != 9:
compWaitList[0] = [[[x,y+1]]] + compWaitList[0]
else:
compWaitList += [[[[x+1,y],[x-1,y]]]]
if y != 0:
compWaitList[0] = [[x,y-1]] + compWaitList[0]
elif compPreviousHits[0][1] == y:
if compPreviousHits[0][0] < x:
compWaitList += [[[x,y-1],[x,y+1]]]
if x != 9:
compWaitList[0] = [[x+1,y]] + compWaitList[0]
else:
compWaitList += [[[x,y-1],[x,y+1]]]
if x != 0:
compWaitList[0] = [[x-1,y]] + compWaitList[0]
compPreviousHits.append(compPreviousHits[0])
compPreviousHits.pop(0)
compPreviousHits = [[x,y]] + compPreviousHits
else:
# Add adjacent coords to the waiting list, depending on position on the grid
if x == 0:
if y == 0:
compWaitList[0] = [[x+1,y]]
compWaitList.append([[x,y+1]])
elif y == 9:
compWaitList[0] = [[x+1,y]]
compWaitList.append([[x,y-1]])
else:
compWaitList[0] = [[x+1,y]]
compWaitList.append([[x,y-1],[x,y+1]])
elif x == 9:
if y == 0:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x,y+1]])
elif y == 9:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x,y-1]])
else:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x,y-1],[x,y+1]])
elif y == 0:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x+1,y],[x,y+1]])
elif y == 9:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x+1,y],[x,y-1]])
else:
compWaitList[0] = [[x-1,y]]
compWaitList.append([[x+1,y],[x,y-1],[x,y+1]])
compPreviousHits.append([x,y])
grids[0].grid = potentialGrid[0]
validMove = True
else:
print("The computer missed!")
grids[0].show()
# Check game is won or not
if grids[0].isDefeated(1) == True:
self.clearScreen()
grids[0].show()
print("Player 2 wins!")
input("Press enter to continue...")
self.clearScreen()
break
self.sunkShips = self.getSunkShips(grids[0],1)
if len(self.sunkShips) >= 1:
print("Computer has sunk...")
for ship in self.sunkShips:
print("Ship of length {}.".format(ship))
else:
print("No ships have yet been sunk.")
input("Press enter for Player 1's turn.")
self.clearScreen()
turn = 1
return
# Function takes in a grid, the opposing side, and the coordinates for the shot
def shoot(self,grid,oSide,shot,isComputer=False):
# Get value in the coord to be shot
coordValue = grid.getCoordValue(shot[0],shot[1])
# If the opponent is the computer
if oSide == 0:
# If value is miss or hit, it is an invalid move
if coordValue == "miss":
print("You've already shot there! Was a miss!")
return "invalid"
elif coordValue == "hit":
print("You've already shot there! Was a hit!")
return "invalid"
# If blank, miss
elif coordValue == "":
print("Miss!")
grid.addPiece([shot],"miss")
return grid.grid
# If computer piece, hit
elif coordValue == 0:
print("Hit!")
grid.addPiece([shot],"hit")
return grid.grid
elif oSide == 1:
if isComputer == True:
if coordValue == "":
grid.addPiece([shot],"miss")
return [grid.grid,"miss"]
elif coordValue == 1:
grid.addPiece([shot],"hit")
return [grid.grid,"hit"]
else:
if coordValue == "miss":
print("You've already shot there! Was a miss!")
return "invalid"
elif coordValue == "hit":
print("You've already shot there! Was a hit!")
return "invalid"
# If shooting at side 2 (own), then it is invalid
elif coordValue == 2:
print("You cannot shoot your own ships!")
return "invalid"
elif coordValue == "":
print("Miss!")
grid.addPiece([shot],"miss")
return grid.grid
# If opponet is 1 and you shoot 1 then it is hit
elif coordValue == 1:
print("Hit!")
grid.addPiece([shot],"hit")
return grid.grid
elif oSide == 2:
if coordValue == "miss":
print("You've already shot there! Was a miss!")
return "invalid"
elif coordValue == "hit":
print("You've already shot there! Was a hit!")
return "invalid"
# If shooting at side 1 (own), then it is invalid
elif coordValue == 1:
print("You cannot shoot your own ships!")
return "invalid"
elif coordValue == "":
print("Miss!")
grid.addPiece([shot],"miss")
return grid.grid
# If opponet is 2 and you shoot 2 then it is hit
elif coordValue == 2:
print("Hit!")
grid.addPiece([shot],"hit")
return grid.grid
# Function takes in a grid, and the number of different ships wanted to add
def makeShips(self,grid,side,shipLengths):
# Add ships of varying lengths
for length in range(len(shipLengths)):
# Adds amount of ships for that length
for amount in range(shipLengths[length]):
ship = self.makeShip(grid,length+2)
grid.addPiece(ship,side)
return grid.grid
# Function returns array of coordinates for a ship
def makeShip(self,grid,length):
ship = []
# Randomise orientation
orientation = random.randint(1,2)
# Whilst valid ship has not been added
while True:
currentShip = []
# Get root position depending on orientation
if orientation == 1:
x = random.randint(0,10-length)
y = random.randint(0,9)
elif orientation == 2:
x = random.randint(0,9)
y = random.randint(0,10-length)
# Add coords depending on length
for i in range(length):
if orientation == 1:
currentShip.append([x+i,y])
elif orientation == 2:
currentShip.append([x,y+i])
# Test that the coords are not filled already
validShip = True
for coord in currentShip:
if grid.isEmpty(coord[0],coord[1]) == False:
# If any coords are filled then the ship is invalid
validShip = False
# If ship is valid then stop trying and return ship coords
if validShip:
keepTrying = False
ship = currentShip
return ship
# Function takes in coordinate inputs
def inputCoord(self,maxX,maxY):
x = -1
y = -1
# While the coordinates are not within grid params
while x < 0 or x > maxX:
try:
x = int(input("Enter X coordinate: "))
except:
x = -1
while y < 0 or y > maxY:
try:
y = int(input("Enter Y coordinate: "))
except:
y = -1
return [x,y]
#Clears the console
def clearScreen(self):
print("\n" * 100)
game = Battleships()