How to create multiple ships?


#1



https://www.codecademy.com/en/courses/python-beginner-en-4XuFm/2/5?curriculum_id=4f89dab3d788890003000096

I'm not sure what to put in the final else statement. I want to create a gameboard, with an "S" at the position of each randomly generated ship. If the position is already an "S", I want to generate another set of random coords for that ship and see if that position is free.

The best I have done is somehow place many more ships on the board then I wanted. I want the number of ships to be equal to a particular value (n) , so it is important for me to know how to create n unique coords for the ships/


from random import randint

def generate_board(size):
    """creates a 2D list
    Input: int
    Output: list"""
    board = [ ]
    row = ["O"] * size
    for col in range(size):
        board.append(row)
    return board

def print_board(board):
    """prints a 2D list as a gameboard
    Input: 2D list
    Output: None"""
    for row in board:
        print " ".join(row)

def validate_int(s):
    """Checks that a string can be converted to an integer
    Input: str
    Output: int"""
    try:
        int(s)
        return True
    except ValueError:
        return False


#The game
print "Let's play Battleship!"

#1 or 2 players?
print "This game is 1 or 2 player."
AI_or_2P = "Player hasn't specified yet!"
while validate_int(AI_or_2P) == False:
    AI_or_2P = raw_input("How many want to play? (Enter 1 or 2):")
    if validate_int(AI_or_2P) == True:
        AI_or_2P = int(AI_or_2P)
        if AI_or_2P != 1 and AI_or_2P !=2:
            print "That's not gonna do buddy. Think about it..."
            print "Try again"
            AI_or_2P = "Not a valid amount of players"
        else:
            print "Okay!"
            break
    else:
        print 'Make sure you type either "1" or "2"'
        print "Try again"

#board_size?
board_size = "No board size!"
while validate_int(board_size) == False:
    board_size = raw_input("What size board will you use? (Enter a number between 1 and 45):")
    if validate_int(board_size) == True:
        board_size = int(board_size)
        if board_size <= 0:
            print "That's not gonna do buddy. Think about it..."
            print "Try again"
            board_size = "Not a valid board size"
        elif board_size == 1:
            print "Genius... Here's the board..."
            board = generate_board(board_size)
            print_board(board)
            break
        elif board_size > 45:
            print "That's too big!"
            board_size = "Not a valid board size"
        else:
            print "Nice! Here's the board!"
            board = generate_board(board_size)
            print_board(board)
            break
    else:
        print "That's not gonna do buddy. Think about it..."
        print "Try again"


if AI_or_2P == 1:
    #difficulty?
    difficulty = "Unspecified difficulty!"
    while validate_int(difficulty) == False:
        if board_size == 1:
            difficulty = 1
            break
        else:
            max_difficulty = (board_size ** 2) / 2
            input_command = "Select a difficuly (Enter a number between 1 and " + str(max_difficulty) + "):"
            difficulty = raw_input(input_command)
            if validate_int(difficulty) == True:
                difficulty = int(difficulty)
                if difficulty < 1:
                    print "That's not gonna do buddy. How about we go for difficulty: 1."
                    print "Try again."
                    difficulty = "player is possibly invalid"
                elif difficulty > max_difficulty:
                    print "If you can't follow these instructions, you certainly don't need a difficulty that high."
                    print "Try again."
                    difficulty = "Extremely non-intelligent"
                elif difficulty == max_difficulty:
                    print "There is always one..."
                    print "Good luck"
                    break
                else:
                    print "Good luck"
                    break
            else:
                print "I recommend difficulty: 1"
                print "Try again"
    print "If nobody finds a ship in the sea, is a ship really there?"
    #ai ship positions?
    ships = difficulty
    ship_positions = generate_board(board_size)
    for ship in range(ships):
        ship_row = randint(0, board_size - 1)
        ship_col = randint(0, board_size - 1)
        if ship_positions[ship_row][ship_col] == "O":
            ship_positions[ship_row][ship_col] = "S"
        else:
            while ship_positions[ship_row][ship_col] == "S":
                ship_row = randint(0, board_size - 1)
                ship_col = randint(0, board_size - 1)


#2

Before loading the board, create a list of random numbers from 0 to n - 1, where n is the number of squares in the grid, For instance, we start with a 5 X 5 grid, so n == 24. The list needn't be that long, just the randrange() set to that range.

num_ships = __number_of_ships__
setup = __board_width__ * __board_height__ - 1
linearized = []
while len(linearized) < num_ships:
    r = random.randrange(setup)
    if r not in linearized:
        linearized.append(r)

Then go through the list and convert the linearized numbers to grid coordinates:

shipN_row = linearized[n] / __board_width__
shipN_col = linearize[n] % __board_width__

#3

I've done something similar, but it's my printout that's not working. It appears '''col''' is being randomized, but for some reason; all '''row'''s get included for each ''''col''''

why is this particular part of the code not working?

def generate_board(size):
"""creates a 2D list
Input: int
Output: list"""
board = [ ]
row = ["O"] * size
for col in range(size):
board.append(row)
return board

def print_board(board):
"""prints a 2D list as a gameboard
Input: 2D list
Output: None"""
for row in board:
print " ".join(row)

def random_coords(space_width, coord_amount):
"""Creates a list of random coordinates for a 2D list
Input: int, int
Output: 2D-list"""
board = generate_board(space_width)
space = space_width ** 2 - 1
linear_random_coords = sample(range(space), coord_amount)
for i in range(coord_amount - 1):
y = linear_random_coords[i] % space_width
x = linear_random_coords[i] / space_width
board[x][y] = "!"
return board

ship_positions = random_coords(board_size, ships)
print_board(ship_positions)

Pick board_size between 0 and 45 and ships less than half the board_size, It doesn't work how it should?


#4

This may or may not be part of the problem but it is worth noting that reference objects preserve their scope and reference, so,

def print_board():
    for i in board:
        print i
board = []
size = 5
row = ['O'] * 5
for i in range(size):
    board.append(row)
print_board()
['O', 'O', 'O', 'O', 'O']
['O', 'O', 'O', 'O', 'O']
['O', 'O', 'O', 'O', 'O']
['O', 'O', 'O', 'O', 'O']
['O', 'O', 'O', 'O', 'O']

Now the kicker:

row[4] = 'X'
print_board()
['O', 'O', 'O', 'O', 'X']
['O', 'O', 'O', 'O', 'X']
['O', 'O', 'O', 'O', 'X']
['O', 'O', 'O', 'O', 'X']
['O', 'O', 'O', 'O', 'X']

Each row of the board isn't a copy of row, it IS row.

for i in range(size):
    board.append(row[:])
print_board()
['O', 'O', 'O', 'O', 'X']
['O', 'O', 'O', 'O', 'X']
['O', 'O', 'O', 'O', 'X']
['O', 'O', 'O', 'O', 'X']
['O', 'O', 'O', 'O', 'X']

row[4] = 'O'
print_board()
['O', 'O', 'O', 'O', 'X']
['O', 'O', 'O', 'O', 'X']
['O', 'O', 'O', 'O', 'X']
['O', 'O', 'O', 'O', 'X']
['O', 'O', 'O', 'O', 'X']

This time any change to row is not applied to board since its rows are shallow copies of, not references to row.

for i in range(len(board)):
    board[i][4] = 'O'
print_board()
['O', 'O', 'O', 'O', 'O']
['O', 'O', 'O', 'O', 'O']
['O', 'O', 'O', 'O', 'O']
['O', 'O', 'O', 'O', 'O']
['O', 'O', 'O', 'O', 'O']

Give this some thought and see if it relates back to your problem.


#5

Gotcha! Thanks so much. As you have pointed out; generating a lists elements so as to be the same mutable element via the appending method means that a mutation of that element, affects all the elements of the list, at least while ... [condition] ( I'm not familiar with the notion of 'shallow copies' yet as I've only just really started computer science)

So, I suppose I could try concatenating [["O"] * n] for each row instead so as to avoid this...

Thanks alot


#6

For pretty output,

def print_board(): 
    for i in board: 
        print ' '.join(i) 

print_board()
O O O O O
O O O O O
O O O O O
O O O O O
O O O O O

A shallow copy is created using the array slice tool, [:]. Working up an example.

a = [
      ['a',
        [
          ['a']
        ]
      ],
      ['b',
        [
          ['b'],['ab'],['ba']
        ]
      ],
      ['c',
        [
          ['c'],['ac'],['bc'],['abc']
        ]
      ],
      ['d',
        [
          ['d'],['ad'],['bd'],['cd'],['abd'],['acd'],['bcd'],['abcd']
        ]
      ]
    ]

print a

[['a', [['a']]], ['b', [['b'], ['ab'], ['ba']]], ['c', [['c'], ['ac'], ['bc'], ['abc']]], ['d', [['d'], ['ad'], ['bd'], ['cd'], ['abd'], ['acd'], ['bcd'], ['abcd']]]]

b = a[:]
print b

[['a', [['a']]], ['b', [['b'], ['ab'], ['ba']]], ['c', [['c'], ['ac'], ['bc'], ['abc']]], ['d', [['d'], ['ad'], ['bd'], ['cd'], ['abd'], ['acd'], ['bcd'], ['abcd']]]]

b.append(['e',
        [
          ['e'],['ae'],['be'],['ce'],['de'],['abe'],['ace'],['ade'],['bce'],['bde'],['cde'],['abce'],['abde'],['acde'],['bcde'],['abcde']
        ]
      ])
print a
print b

[['a', [['a']]], ['b', [['b'], ['ab'], ['ba']]], ['c', [['c'], ['ac'], ['bc'], ['abc']]], ['d', [['d'], ['ad'], ['bd'], ['cd'], ['abd'], ['acd'], ['bcd'], ['abcd']]]]

[['a', [['a']]], ['b', [['b'], ['ab'], ['ba']]], ['c', [['c'], ['ac'], ['bc'], ['abc']]], ['d', [['d'], ['ad'], ['bd'], ['cd'], ['abd'], ['acd'], ['bcd'], ['abcd']]], ['e', [['e'], ['ae'], ['be'], ['ce'], ['de'], ['abe'], ['ace'], ['ade'], ['bce'], ['bde'], ['cde'], ['abce'], ['abde'], ['acde'], ['bcde'], ['abcde']]]]