This is an adaption of a single player Battleship game. I made it two player, and I also tried to implement OOP and DRY principles. I would like my code to be reviewed for OOP, DRY, PEP8, and overall best practices. I am new to development (about 1 month strong), so any constructive feedback would be greatly appreciated.
One of my biggest light-bulb moments was when I realized that I was stuck in a loop because my loop method was a numeric value outside of the class method. Once I put it in the class method and made it a list, I could then pass the data back and forth and get a count of each players tries by using
from random import randint class Person(object): def __init__(self, name, turn, loop): self.name = name self.turn = turn self.loop = loop @classmethod def create(cls, turn): while True: name = input("\nWhat is the name of Player %s? " % turn) if name.isalpha(): break print("\nPlease share your name with me.\n") print("\nNice to meet you %s. " % name) print("It will be fun to play Battleship!\n") loop =  return cls(name, turn, loop) @staticmethod def welcome(name1, turn1, name2, turn2): print("It's decided that") print("%s will take the %sst turn" % (name1, turn1)) print("and %s will take the %snd turn." % (name2, turn2)) def salutation(name1, name2, loop1, loop2): if (len(loop1)) and (len(loop2)) == 5: print("Thanks for playing %s and %s." % (name1, name2)) print("Hopefully we will play again, soon!") elif (len(loop1)) > (len(loop2)): print("Excellent win, %s!" % name1) print("Better luck next time, %s." % name2) else: print("Excellent win, %s!" % name2) print("Better luck next time, %s.\n" % name1) class Board(object): def __init__(self, surface, squares): self.surface = surface self.squares = squares @classmethod def create(cls, name): while 1: squares = input("\n%s, how big would you like your board to be (3-5)? " % name) try: squares = int(squares) except (TypeError, ValueError): print("\nPlease enter a number between 3 and 5.") continue if squares >= 3 and squares <= 5: break surface =  for i in range(squares): surface.append((["O"] * squares)) return cls(surface, squares) def random_row(surface): return randint(1, len(surface)) def random_col(surface): return randint(1, len(surface)) def rules(): print("\nIn this game, you will pick a number") print("between 1 and your board length for each row and") print("coloumn. Then, if your guess matches") print("the randomly generated location. You win.") print("Each player has 5 attempts to guess correctly.") def one_play(name, turn, surface, row, col, loop): print("\nOk, %s. Go ahead and take turn %s." % (name,(len(loop)+1))) print_board(name, surface) guess_row = (input("Guess Row (1-%s): " % len(surface))) guess_col = (input("Guess Col (1-%s): " % len(surface))) try: guess_row = int(guess_row) guess_col = int(guess_col) if guess_row == row and guess_col == col: print("\nCongrats! You sunk my Battleship!\n") surface[(guess_row)-1][(guess_col)-1] = "B" loop.extend((1, 2, 3, 4, 5, 6)) elif ((guess_row < 1 or guess_row > (len(surface))) or (guess_col < 1 or guess_col > (len(surface)))): print("\nOops, that's not even on the board.\n") elif (surface[(guess_row)-1][(guess_col)-1] == "X"): print("\nYou guessed that one already.\n") else: print("\nYou missed my Battleship!\n") surface[(guess_row)-1][(guess_col)-1] = "X" except (TypeError, ValueError): print("\nYou failed to answer the question correctly.") loop.append(1) return (surface, loop) # These are functions, not methods def print_board(name, surface): print("\nHere is the board for %s." % name) for i in surface: print(" ".join(i)) print("") def play_battleship(): print("\n\n\n\nWelcome to Battleship!") # We take the user input and create the Players Player1 = Person.create(1) Player2 = Person.create(2) # Assign the names to variables name1 = Player1.name name2 = Player2.name # Assign each player's turn. turn1 = Player1.turn turn2 = Player2.turn # It's always good to say, "Hello." Person.welcome(name1, turn1, name2, turn2) # We create the boards, which are lists. Board1 = Board.create(name1) Board2 = Board.create(name2) # Print the rules. Board.rules() # Store the random row and column in a variable for each player. ship_row1 = Board.random_row(Board1.surface) ship_col1 = Board.random_col(Board1.surface) ship_row2 = Board.random_row(Board2.surface) ship_col2 = Board.random_col(Board2.surface) # Place each player's list in a variable. surface1 = Board1.surface surface2 = Board2.surface # Keep track of each player's loop for flow control with a list. loop1 = Player1.loop loop2 = Player2.loop while (len(loop1) < 5) and (len(loop2) < 5): Board.one_play(name1, turn1, surface1, ship_row1, ship_col1, loop1) # Here, we check the length of loop1 to see if Player1 won. # If so, we break. the loop. if (len(loop1))>= 6: break else: Board.one_play(name2, turn2, surface2, ship_row2, ship_col2, loop2) Person.salutation(name1, name2, loop1, loop2) # Ask the player to play again. while 1: again = input("\n\nWould you like to play again: ") if "y" in again.lower(): play_battleship() else: break play_battleship()