HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonModerate

Battleships game in Python

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
gamepythonbattleships

Problem

The idea is, you enter an amount of ships, for example 10. It will then place 10 ships on a 10x10 grid (0-9). You may enter a coordinate, and it will tell you if it is a hit or a miss. A simplified version of the well-known board game.

  • C indicates 'Computer'. These are not shown on the grid, but are stored in the 2D array.



  • H indicates 'Hit'. Self explanatory.



  • M indicates 'Miss'. Also self explanatory.



  • Null / whitespace indicates that nothing has been set at that coordinate.



I created this program with the intent of making it as efficient and readable as possible. It's difficult to explain the inner workings of my mind, so I apologize for no comments.

best ran outside of IDE (in console)

code:

```
import random, os

ships = int(input("Enter ship count: "))
gridSize = 10
game = [[' ' for x in range(gridSize)] for y in range(gridSize)]

def getGrid(x,y):
return game[x][y]

def setGrid(x,y,char):
game[x][y] = char

for i in range(ships):
x = random.randint(0,gridSize-1)
y = random.randint(0,gridSize-1)
setGrid(x,y,'C')

xLabel = " " * 3
for i in range(gridSize):
xLabel += str(i) + " "

result = "Make a move!"
hits = 0

while hits != ships:
os.system("cls")
print(" " + result + " [Ships: " + str(ships) + ", Size: " + str(gridSize) + ", Hits: " + str(hits) + "]\n")
print(xLabel)
for x in range(gridSize):
print(" " + str(x) + " ",end="")
for y in range(gridSize):
print(" " * 2 if getGrid(x,y) == 'C' else getGrid(x,y) + " ",end="")
print("")

xGuess = int(input("\n X: "))
yGuess = int(input(" Y: "))

if getGrid(xGuess,yGuess) == 'C':
result = "Hit! (" + str(xGuess) + ":" + str(yGuess) + ")"
setGrid(xGuess,yGuess,'H')
hits += 1
else:
result = "Miss! (" + str(xGuess) + ":" + str(yGuess) + ")"
setGrid(xGuess,yGuess,'M')

print("\nCongratulations, you won the game!")
os.sy

Solution

Your program is pretty good, imports at the top, some functions and comprehensions.
But they can all be improved.

  • To adhere to PEP8 you should only import one module per import.



  • You need some more functions, the ones your are using I would not use and would infuriate me.



-
This is more an unwritten notion, but _ is known as the throw away variable.
Since you throw away the range's items you could do:

[' ' for _ in range(gridSize)]


I'd change the way you display the rows, currently you display two characters per print,
this is quite hard to read, and not very efficient.
Instead I'd recommend a list comprehension!

You display a space if getGrid(x,y) is "C", otherwise getGrid(x,y), for each character in that row.
So:

row = [" " if getGrid(x, y) == "C" else getGrid(x, y) for y in range(gridSize)]


To then display this row you should use ' '.join(row). This puts a single space in-between each position on the board.

The other major change I'd make is the use of str.format.
Take the following line:

print(" " + result + " [Ships: " + str(ships) + ", Size: " + str(gridSize) + ", Hits: " + str(hits) + "]\n")


That's hard to read with all the string concatenations and conversion, which str.format tidies up.
The equivalent would be:

print(" {} [Ships: {}, Size: {}, Hits: {}]\n".format(result, ships, gridSize, hits))


You can now tell the format of the string, and can reasonably guess what will go there.
All in little to no time.

For a few additional changes I'd recommend you change range with enumerate to get the x positions and the row, where you display the grid.
And I'd completely remove the range when using the row comprehension.
To show this:

for x, row in enumerate(game):
    print(' {} {}'.format(x, ' '.join([" " if y == "C" else y for y in row])))


I would also change the boards variable name to board so that people can instantly understand what you mean when you reference it.

In Python you should use snake_case not pascalCase for functions and variables, so gridSize should be grid_size.

And finally, you should use a try-except block when you are converting user input to a number as CAD97 put in their answer.
This is as your program will crash if I enter "a".

To improve the xLabel creation you can think of it like we did the row, however you display the item you get from the range. This means that the list comprehension that you'd do is:

row = [str(i) for i in range(gridSize)]
xLabel = " " * 3 + " ".join(row)

Code Snippets

[' ' for _ in range(gridSize)]
row = [" " if getGrid(x, y) == "C" else getGrid(x, y) for y in range(gridSize)]
print(" " + result + " [Ships: " + str(ships) + ", Size: " + str(gridSize) + ", Hits: " + str(hits) + "]\n")
print(" {} [Ships: {}, Size: {}, Hits: {}]\n".format(result, ships, gridSize, hits))
for x, row in enumerate(game):
    print(' {} {}'.format(x, ' '.join([" " if y == "C" else y for y in row])))

Context

StackExchange Code Review Q#125781, answer score: 14

Revisions (0)

No revisions yet.