**This question is a follow-up question of [this one](https://codereview.stackexchange.com/questions/73603/simple-1-player-battleships-game).**

I've learnt a bit more Python since then. I recently came back to making [battleships](http://en.wikipedia.org/wiki/Battleships_(game)) again, and I think I did better. 

These are the changes I've made:

1. Completely rewritten.
2. Multiple battleships.
3. Removed difficulty settings (as the way I was doing it before wouldn't apply to multiple battleships)
4. Separated UI and game logic

This is the code:

```
"""A simple in-interpreter game of battleships."""
__version__ = "2.0.5"

from random import randint
from math import sqrt

def getBoolFromInput(prompt, defaultReturn=None, yes=(), no=()):
    """Returns True / False / None from user input"""
    inp = "".join((char for char in input(prompt).lower() if char.isalpha()))
#   inp is now all the alphabetic characters in the input
    if inp in ("yes", "y", "yh", "definitely") or inp in yes:
        return True
    elif inp in ("no", "n", "nope") or inp in no:
        return False
    return defaultReturn

def getOrdinal(num):
    """Returns an ordinal string of the number that is passed.
 (e.g. 1 --> "1st", -32 --> "-32nd", etc."""
    negative = False
    if num < 0:
        num *= -1
        negative = True
    unit = num % 10
    if 4 <= num <= 20 or 4 <= unit <= 9 or unit == 0 or num == 0:
        suffix = "th"
    else:
        suffix = ["st", "nd", "rd"][num % 10 - 1]
    if negative:
        num *= -1
    return str(num) + suffix

def win(gameState):
    print("Well done! You won battleships! That was your",
          "{} time today.".format(getOrdinal(gameState.gamesWon)))
    print("You have played {} games so far, losing {}.".format(
        gameState.gamesPlayed, gameState.gamesLost * -1))
    return endText(gameState)

def lose(gameState):
    print("Oh no! You lost! That was your {} time today. Better luck".format(
        getOrdinal(gameState.gamesLost * -1)), "next time!")
    print("You have played {} games so far, winning {}.".format(
        gameState.gamesPlayed, gameState.gamesWon))
    return endText(gameState)

def endText(gameState):
    print("This give you {}% win and you have a {} game win streak.".format(
        gameState.gamesWon // gameState.gamesPlayed * 100,
        gameState.winStreak))
    while True:
        inp = getBoolFromInput("Would you like to play again? ",
                               None, ("again"), ("notagain"))
        if inp is None:
            print("Please type yes or no.")
            continue
        return inp

class Board:
    def __init__(self, height, length):
        self.height = height
        self.length = length
        self.items = []
        for i in range(height):
            self.items.append([])
            for i2 in range(length):
                self.items[i].append({"Current mark": "-",
                                      "Has battleship": False})

    def __repr__(self):
        """Returns the board with " " between every comlumn and "\\n"
 between every row."""
        return "\n".join([" ".join([self.items[h][l]["Current mark"] for h in
                                    range(self.height)]) for l in range(
                                        self.length)])

    def updateMark(self, x, y):
        if x > self.length or y > self.height or x < 1 or y < 1:
            return "OUT OF OCEAN"
        x -= 1
        y -= 1
#       -1 so a user's (1, 1) turns into an array index [0][0]
        if self.items[y][x]["Current mark"] != "-":
            return "ALREADY HIT"
        elif self.items[y][x]["Has battleship"]:
            self.items[y][x]["Current mark"] = "B"
            return "HIT"
        self.items[y][x]["Current mark"] = "X"
        return "NO HIT"

    def revealBoard(self):
        for y in range(self.height):
            for x in range(self.length):
                if self.items[y][x]["Has battleship"]:
                    self.items[y][x]["Current mark"] = "B"
                elif self.items[y][x]["Current mark"] == "-":
                    self.items[y][x]["Current mark"] = "O"

class GameState:
    def reset(self, turns, battleships):
        self.turnsTotal = turns
        self.turn = 0
        self.turnsRemaining = turns
        self.battleships = battleships
        self.battleshipsHit = 0
        self.battleshipsRemaining = battleships

    def __init__(self, disableConfirmations):
        self.disableConfirmations = disableConfirmations
        self.gamesPlayed = 0
        self.gamesWon = 0
        self.gamesLost = 0
        self.winStreak = 0

    def nextTurn(self):
        """Increments turn and checks if the game has ended"""
        self.turn += 1
        self.turnsRemaining -= 1
        if self.turnsRemaining == 0:
            return self.gameFinished(False)
        return True

    def hitBattleship(self):
        """Increments amount of battleships hit and checks if all have
 been hit"""
        self.battleshipsHit += 1
        self.battleshipsRemaining -= 1
        if self.battleshipsRemaining == 0:
            return self.gameFinished(True)
        return True

    def gameFinished(self, result):
        self.gamesPlayed += 1
        if result:
            if self.winStreak < 1:
                self.winStreak = 0
            self.winStreak += 1
            self.gamesWon += 1
            return win(self)
        else:
            if self.winStreak > -1:
                self.winStreak = 0
            self.winStreak -= 1
            self.gamesLost -= 1
            return lose(self)

class Battleship:
    def __init__(self, board, size):
        while True:
            east = True
            self.x = randint(0, board.length - size)
            self.y = randint(0, board.height - size)
            self.direction = randint(0, 1)
            valid = True
            if self.direction == randint(0, 1):
                self.direction = "EAST"
                x = self.x
                for i in range(size):
                    if board.items[self.y][x]["Has battleship"]:
                        valid = False
                    x += 1
            else:
                self.direction = "SOUTH"
                east = False
                y = self.y
                for i in range(size):
                    if board.items[y][self.x]["Has battleship"]:
                        valid = False
                    y += 1
            if valid:
                if east:
                    x = self.x
                    for i in range(size):
                        board.items[self.y][x]["Has battleship"] = True
                        x += 1
                else:
                    y = self.y
                    for i in range(size):
                        board.items[y][self.x]["Has battleship"] = True
                        y += 1
                break

def main():
    try:
        print(u"""Hello! Welcome to battleships, version 2.0.
Changelog:
- Complete rewrite
  - The game has multiple battleships now!
  - No longer any difficulty setting (because of said multiple battleships)
  - Now with \u221E% more classes!

Now, let's get started!
""")
    except UnicodeEncodeError:
        print("""Hello! Welcome to battleships, version 2.0.
Changelog:
- Complete rewrite
  - The game has multiple battleships now!
  - No longer any difficulty setting (because of said multiple battleships)
  - Now with infinity% more classes!

Now, let's get started!
""")
    gameState = GameState(getBoolFromInput("Would you like to disable " +
                                           "confirmation messages? ", False))
#   Main game loop
    while True:
        s = "tall"
        while True:
            inp = input("How {} would you like the board? ".format(s))
            try:
                inp = int(inp)
            except ValueError:
                print("Please type in a number!")
                continue
            if inp < 4:
                print("Please type a value greater than 3!")
            elif inp > 25:
                if not getBoolFromInput("Do you really want a board dimensio" +
                                        "n this big?", False):
                    continue
            if s == "tall":
                s = "long"
                height = inp
            else:
                length = inp
                if not gameState.disableConfirmations:
                    a = getBoolFromInput("Are you alright with a " +
                                         "{} by {} board? ".format(height,
                                                                   length))
                    if a is None:
                        print("I'll take that as a \"Yes\"")
                    elif a:
                        print("OK! Let's continue!")
                    else:
                        s = "tall"
                        print("Try again then.")
                        continue
                break
        board = Board(height, length)
        maxBattleships = int(0.5*sqrt(8*(height*length) + 1) - 0.5) - 2
#       Finds which triangle number the area of the board is inbetween
#       so you can't put more battleships than the board can take.
        while True:
            print("You can have a maximum of", maxBattleships, "battleships",
                  "with your board size.")
            inp = input("How many battleships would you like? ")
            try:
                inp = int(inp)
            except ValueError:
                print("Please type a number!")
                continue
            if inp < 1:
                print("Please enter a value greater than 0!")
                continue
            elif inp > maxBattleships:
                print("Please type a value less than", maxBattleships)
                continue
            if not gameState.disableConfirmations:
                a = getBoolFromInput("Are you alright with " + str(inp) +
                                     " battleships? ")
                if a is None:
                    print('I\'ll take that as a "Yes"')
                elif a:
                    print("OK! Let's continue!")
                else:
                    print("Try again then.")
                    continue
            break
        battleships = inp
        while True:
            inp = input("How many turns would you like? ")
            try:
                inp = int(inp)
            except ValueError:
                print("Please type a number!")
                continue
            if inp < 1:
                print("Please enter a value greater than 0!")
                continue
            if not gameState.disableConfirmations:
                a = getBoolFromInput("Are you alright with " + str(inp) +
                                     " turns? ")
                if a is None:
                    print("I'll take that as a \"Yes\"")
                elif a:
                    print("OK! Let's continue!")
                else:
                    print("Try again then.")
                    continue
            break
        gameState.reset(inp, (battleships * (battleships + 1)) // 2)
#                             This equation shows which triangle number
#                             the battleship represents
        allBattleships = {}
        for i in range(battleships, 1, -1):
            allBattleships[i] = Battleship(board, i)
#           This makes the battleships increase in size, with their
#           lengths like (1, 2, 3) or (1, 2, 3, 4) etc.
        playing = True
        while playing:
            if gameState.turn == 0:
                print("Let's play Battleships!\nTurn 1, {} turn{}".format(
                    gameState.turnsRemaining, "".join(
                        ["" if gameState.turnsRemaining == 1 else "s"
                         for i in (1,)])), "remaining.")
            elif gameState.turnsRemaining == 1:
                print("Last turn!")
            else:
                print("Turn {}, {} turns remaining.".format(
                    gameState.turn, gameState.turnsRemaining))
            print(board)
            s = "Y"
            while True:
                inp = input(s + " coordinate: ")
                try:
                    inp = int(inp)
                except ValueError:
                    print("Please type a valid number!")
                    continue
                if s == "Y":
                    s = "X"
                    x = inp
                    continue
                y = inp
                if not gameState.disableConfirmations:
                    inp = not getBoolFromInput("Are you sure that you want" +
                                               "to aim for {}, {}? ".format(
                                                   x, y), True)
                    if inp:
                        print("Try again then.")
                        s = "Y"
                        continue
                break
            aim = board.updateMark(x, y)
            if aim == "OUT OF OCEAN":
                print("That's not even in the ocean...")
            elif aim == "ALREADY HIT":
                print("You already hit that one.")
            elif aim == "NO HIT":
                print("You didn't hit anything...")
            else:
                print("Congratualtions, you hit part of a battleship!")
                cont = gameState.hitBattleship()
            if gameState.battleshipsRemaining != 0:
                cont = gameState.nextTurn()
            if gameState.turnsRemaining != 0 and \
               gameState.battleshipsRemaining != 0:
                continue
            else:
                board.revealBoard()
                print("\n" + str(board) + "\nThese were the battleships!")
            playing = False
            if cont:
                print("\n\nRestarting...\n\n")
                continuing = True
            else:
                continuing = False
        if continuing:
            continue
        print("Thanks for playing!")
if __name__ == "__main__":
    main()

```

I made it in a few hours (~3 to 4), so there are probably are some bugs; however, the game plays fine, and I can't find any bugs. I would like some feedback on 2 things:

1. if the classes are necessary for readability or just confusing
2. if my documentation using comments and docstrings is good enough.

Notes: 

1. I am using camelCase over underscore\_case as I prefer it, and find it more readable.
2. I am repeating many similar `while True:` loops as they are so very slightly different, that I couldn't get around it.