Is there any better way to optimalize this battleships "shot" function? - python

I'm trying to make a simple approach to a battleships-bot in python, but I'm having some issues regarding my "shot" function repeating the same coordinates. Here are some of my code:
import numpy as np
import random as rnd
numberlist = [1,3,5,7,9]
numberlist2 = [0,2,4,6,8]
my_shots = np.array([
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
])
def letter_to_column(letter):
"""Takes in lettervalue, and returns what column it corresponds to,
zero-indexed.
Example:
letter_to_column('A') # => 0
"""
return "ABCDEFGHIJ".index(letter.upper())
def find_value(coordinatestring):
"""Takes in a coordinatestring in the form "letternumber" and returns wheter it is a boat
in the coordinate or not
"""
letter = coordinatestring[0]
row = int(coordinatestring[1])
col = letter_to_column(bokstav)
value = my_shots[row][col]
return value
This is the function that repeats the places it shoots, I would appreciate if anyone has any solution to how to make the function not repeat the "lettervalues" it has already given.
def shot(letter = "A", number = "0"):
# Chekcs if I already have shot there
while find_value(letter + number) == 1:
letter = rnd.choice("ABCDEFGHIJ")
# Uses the "chess method" by only shooting on every other route
if letter_to_column(letter) % 2 == 0:
number = str(rnd.choice(numberlist2))
else:
number = str(rnd.choice(numberlist))

If the board is "small" like in Battleship, one strategy you could employ is to generate all the shots at once and then just shuffle them. This works like a champ when you have only 100 locations and avoids a clunky "while this_shot != last_shot" type of setup.
import random
letters = list('ABCDEFGHIJ')
nums = range(9)
all_shots = [''.join([str(num), letter]) for num in nums for letter in letters]
random.shuffle(all_shots)

Related

Why do we need to swap grid_model and next_grid_model?

This is a part of code of John Conway's GAME OF LIFE
import random
height = 100
width = 100
def randomize(grid, width, height):
for i in range(0, height):
for j in range(0, width):
grid[i][j] = random.randint(0,1)
grid_model = [0] * height
next_grid_model = [0] * height
for i in range(height):
grid_model[i] = [0] * width
next_grid_model[i] = [1] * width
def next_gen():
global grid_model, next_grid_model
for i in range(0, height):
for j in range(0, width):
cell = 0
count = count_neighbors(grid_model, i, j)
if grid_model[i][j] == 0:
if count == 3:
cell = 1
elif grid_model[i][j] == 1:
if count == 2 or count == 3:
cell = 1
next_grid_model[i][j] = cell
temp = grid_model
grid_model = next_grid_model
next_grid_model = temp
def count_neighbors(grid, row, col):
count = 0
if row-1 >= 0:
count = count + grid[row-1][col]
if (row-1 >= 0) and (col-1 >= 0):
count = count + grid[row-1][col-1]
if (row-1 >= 0) and (col+1 < width):
count = count + grid[row-1][col+1]
if col-1 >= 0:
count = count + grid[row][col-1]
if col + 1 < width:
count = count + grid[row][col+1]
if row + 1 < height:
count = count + grid[row+1][col]
if (row + 1 < height) and (col-1 >= 0):
count = count + grid[row+1][col-1]
if (row + 1 < height) and (col+1 < width):
count = count + grid[row+1][col+1]
return count
glider_pattern = [[0, 0, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 1, 0],
[0, 1, 1, 1, 0],
[0, 0, 0, 0, 0]]
glider_gun_pattern = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0],
[0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
def load_pattern(pattern, x_offset=0, y_offset=0):
global grid_model
for i in range(0, height):
for j in range(0, width):
grid_model[i][j] = 0
j = y_offset
for row in pattern:
i = x_offset
for value in row:
grid_model[i][j] = value
i = i + 1
j = j + 1
if __name__ == '__main__':
next_gen()
temp = grid_model
grid_model = next_grid_model
next_grid_model = temp
What I want to ask is why we need to swap grid_model and next_grid_model?
Consider this small example
000
111
It should be followed by this grid
010
010
upper corners are 0, and surrounded by two 1, so they remain 0
lower corners are 1, and surrounded by one 1, so they become 0
upper middle pixel is 0 and surrounded by three 1, so it becomes 1.
lower middle pixel is 1 and surrounded by two 1, so it stays 1
Now, what would happen if you were using only one grid, that is compute this in situ:
You would start with grid
000
111
First pixel is 0, surround by two 1, so it stays 0, and grid stays
000
111
Second pixel is 0 surrounded by three 1, so it becomes 1. And grid becomes
010
111
Third pixel is 0, surrounded by three 1, so it becomes 1, and grid becomes
011
111
Fourth pixel (on 2nd line) is 1, surrounded by two 1, it stays 1.
011
111
Fifth pixel is 1, surrounded by four 1, it becomes 0
011
101
At last, sixth pixel is 1, surrounded by two 1, it stays 1
011
101
So, result is completely different from the expected result.
You get grid
011
101
Instead of grid
010
010
That is not directly the answer to your question. It is the answer to "why can't I just compute using a single grid, and why I need to compute a next_grid from a current_grid".
And my answer shows that you need to compute a new grid from a current grid.
Now, you have two options to do so.
Either at each step you allocate and compute a new grid, that, once computed becomes the current grid, and the former current grid is simply forgotten. That force you to redo the next_grid_model = [0] * height and for i in range(height): next_grid_model[i] = [1] * width each time. But it would work
Or, you don't want to bother creating a brand new next_grid_model at each step. So you just reuse the grid_model variable, that still exists, and whose, after all, you don't need any more, since you intend to replace it by next_grid_model.
That is the reason for the swap:
next_grid_model becomes grid_model, because, that is the game (I take you get that part. Again, from the first part of my answer, you probably understand why we need to compute a next_grid_model instead of updating grid_model directly. But then next_grid_model, as its name indicate, becomes the new grid_model.
And we need to create a new 2d-array to be the new next_grid_model. And if we don't want to create it from scratch, one solution is to say that the old grid_model will host the future next_grid_model. Hence the swap.
Note that such a trick is more important in other languages than in python. In C, for example, to allocate both grid, we would have to call malloc.
And calling malloc to create a new grid at each stage, while calling free on the former grids is a little bit stupid (allocating exactly the same amount of memory we are freeing — without free, it is even worse, and a memory leak). So it is quite classical in such case, to swap the "current" and "future" memories, instead of allocating a new one each time.
In python, if you were to just create a new next_grid_model each time, it would also work. Garbage collector would take care of all the former grids that you dropped, and everything would be fine. But even if it would work, that still would mean that you have to to the job of creating a new next_grid_model each time.
So a shorter answer could have been:
at each stage you need either to
next_grid_model = [0] * height
for i in range(height):
next_grid_model[i] = [1] * width
Or to
# Just a swap (I write it that way, because it is more compact,
# but it is the same thing as your 3 lines involving a temp variable)
next_grid_model, grid_model = grid_model, next_grid_model
Both works. They are just two way to create a future next_grid_model. Swap is simply faster.

How can I draw a pattern without using any third party libraries, and the sequence of input

I need to draw a pattern in the image given below without using any libraries in python.
Input: 3 4 3 4 5 1 9 2 2 4
Image:
Below is the code i tried but it does not working
def triangle(*args):
main=[]
for num in range(len(args)):
k = args[num] - 1
patt = []
for i in range(0, args[num]):
hello = ''
for j in range(0, k):
hello = hello + " "
if num%2==0:
k = k - 1
hello = hello+"/"
else:
k = k+ 1
hello = hello + "\\"
patt.append(hello)
main.append(patt)
return main
lists = triangle(3,4,3)
I don't know if I understand your question, but you could print it to the terminal without any modules.
In order to fit in bigger figures, you have to modify the length and amount of the rows. The lines don't connect perfectly, but you can definitely see the graph.
number_input = [4, 3, 2, 2, 5]
# default pattern
pattern = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
height = 5 # starting row (adaptable)
rows = 14 # rows of the pattern
lines = 0 # lines added to the pattern
def figure(height, lines, nums):
print(nums)
for i in range(len(nums)):
# if i is even it adds '/'
if i % 2 == 0:
for j in range(nums[i]):
pattern[rows-height-1][lines] = '/'
height += 1 # increase height by 1
lines += 1 # count the lines added to the pattern
height -= 1
# if i is odd it adds '\'
elif i % 2 == 1:
for j in range(nums[i]):
pattern[rows-height-1][lines] = '\\'
height -= 1 # decrease height by 1
lines += 1
height += 1
figure(height, lines, number_input)
# replace zeros with spaces
for line in pattern:
for i, num in enumerate(line):
if num == 0:
line[i] = ' '
# print the lines
print(*line)
Hope I could help.

python nested list value change not working

for i in range(len(arr)):
temps = [[0]*9]*3
# temp = [[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0,
0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]]
when tries
temps[0][0] = 4
gives this output
[[4, 0, 0, 0, 0, 0, 0, 0, 0], [4, 0, 0, 0, 0, 0, 0,
0, 0], [4, 0, 0, 0, 0, 0, 0, 0, 0]]
tries to change the value of temp[0][0]
it changes all temp[0][0] temp[0][1] temp[0][2]
value
Christian is right. [t] * 3 is equivalent to [t, t, t]
You can solve it by doing this:
for i in range(len(arr)):
temps = [[0]*9 for _ in range(3)]

Why do I get and error when my loop runs the 2nd time? TypeError: 'int' object has no attribute '__getitem__'

The aim of this code is to change the numbers on the board according to given moves.
This is a simplified excerpt from my code and I'd like the principle to stay the same.
It seems like the code runs through the first loop but then gives an error when it runs through it another time: TypeError: 'int' object has no attribute 'getitem'
Help would be appreciated.
import numpy
board = numpy.array([[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 2, 0, 0, 0],
[0, 0, 0, 2, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0]])
boardlist0 = [board]*2
boardlist1 = []
ind = 0
move = [[0,0], [7,4]]
for k in move:
move = move[ind]
boardlist0[ind][move[0]][move[1]] = 1
boardlist1.append(boardlist0)
ind += 1
ind = 0
move = [[0,0], [7,4]]
for k in move:
move = move[ind]
print(move)
prints
[0, 0]
0
On the second iteration, move equals 0. So
move[0]
raises a TypeError.
I'm not quite sure what the intention of your code is, but you could avoid the TypeError using k instead of move. (Below I've renamed move --> moves, and k --> move):
import numpy
board = numpy.array([[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 2, 0, 0, 0],
[0, 0, 0, 2, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0]])
boardlist0 = [board]*2
moves = [[0,0], [7,4]]
for move, board in zip(moves, boardlist0):
board[move[0], move[1]] = 1
for board in boardlist0:
print(board)
Note that boardlist = [board]*2 makes a 2-element list where each element references the exact same object -- not a copy of -- board. Thus, altering boardlist0[0] affects boardlist0[1], and vice versa. If instead you want two independent boards, use
boardlist0 = [board.copy() for i in range(2)]

2d array of zeros

There is no array type in python, but to emulate it we can use lists. I want to have 2d array-like structure filled in with zeros. My question is: what is the difference, if any, in this two expressions:
zeros = [[0 for i in xrange(M)] for j in xrange(M)]
and
zeros = [[0]*M]*N
Will zeros be same? which one is better to use by means of speed and readability?
You should use numpy.zeros. If that isn't an option, you want the first version. In the second version, if you change one value, it will be changed elsewhere in the list -- e.g.:
>>> a = [[0]*10]*10
>>> a
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
>>> a[0][0] = 1
>>> a
[[1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
This is because (as you read the expression from the inside out), you create a list of 10 zeros. You then create a list of 10 references to that initial list of 10 zeros.
Note that:
zeros = [ [0]*M for _ in range(N) ] # Use xrange if you're still stuck in the python2.x dark ages :).
will also work and it avoids the nested list comprehension. If numpy isn't on the table, this is the form I would use.
for Python 3 (no more xrange), the preferred answer
zeros = [ [0] * N for _ in range(M)]
for M x N array of zeros
In second case you create a list of references to the same list. If you have code like:
[lst] * N
where the lst is a reference to a list, you will have the following list:
[lst, lst, lst, lst, ..., lst]
But because the result list contains references to the same object, if you change a value in one row it will be changed in all other rows.
Zhe Hu's answer is the safer one and should have been the best answer. This is because if we use the accepted answer method
a = [[0] * 2] * 2
a[0][0] = 1
print(a)
will give the answer
[[1,0],[1,0]]
So even though you just want to update the first row first column value, all the values in the same column get updated. However
a = [[0] * 2 for _ in range(2)]
a[0][0] = 1
print(a)
gives the correct answer
[[1,0],[0,0]]

Categories

Resources