2 Replies - 7719 Views - Last Post: 12 April 2011 - 05:33 AM Rate Topic: -----

#1 mazloum91  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 11-April 11

Battleship in Python

Posted 11 April 2011 - 04:21 PM

So i have to write a battleship game with already given source module and i have to modify i code that calls the source module. My problem is that i don't know how to check if a spot is already used. As in when placing ships i dont know how to check if they overlap, also when firing i don't know how to check to make sure the spot hasn't already been fired on.


THIS IS THE SOURCE CODE:

# Side.py
# -- Module for implementing each player (side) in the game of battleship.
#    This is a translation of Dr. Ken Wong's Side.pm perl module, version 1.5.
#    This has created for CMPUT 174.

# Module gobal
Sides = {}

# This abstract data structure maintains information about a player's
# "ocean grid" in the game of Battleship.  A player's ocean grid is fired
# upon by the other player.  We call this structure a "side", and it 
# tracks the position of placed ships, hits and misses, as well as free 
# locations that have not yet been fired upon.

# To establish a side, you call the NewSide subroutine, which creates a 
# new side, and returns a "handle" to the created side.  You use that 
# handle again in the other subroutines to deal with the associated side.

# A side has a coordinate system.  A full coordinate value must be 
# expressed like the string "A:1".  Here, "A" is the vertical coordinate 
# (an uppercase letter) and 1 is the horizontal coordinate.

# Users of this package must not depend on how the data for a side
# is actually represented.

# Only the following access routines may be called:
# ACCESS ROUTINES

def NewSide(ID, maxcoord_v, maxcoord_h):
    """
    side = Side.NewSide(maxcoord_v, maxcoord_h)

    Side.NewSide returns a handle to a new side structure for the given 
    maximum vertical maxcoord_v and horizontal maxcoord_h coordinates; 
    otherwise returns 'None' if unable to.  The largest board allowed is 
    25x25, i.e., "Y" as the maximum vertical coordinate and 25 as the 
    maximum horizontal coordinate.
    """
    # allow game areas up to 25 x 25
    if ord(maxcoord_v) < ord('A') or ord(maxcoord_v) > ord('Y'):
        return None
    if maxcoord_h < 1 or maxcoord_h > 25:
        return None

    # set up side structure
    side = {'maxcoord_v': maxcoord_v,
            'maxcoord_h': maxcoord_h,
            'free': {},
            'fleet': {},
            'hit': {},
            'missed': {},
            'ID': ID
            }

    # initialize 'free' coordinates
    for i in range(ord('A'), ord(maxcoord_v)+1):
        for  j in range(1, maxcoord_h + 1):
            temp_coord = chr(i) + ':' + str(j)
            side['free'][temp_coord] = 1

    Sides[ID] = 1

    return side

def GetMaxCoord_Vertical(side):
    """
    maxcoord_v = Side.GetMaxCoord_Vertical(side)

    Side.GetMaxCoord_Vertical returns the maximum vertical coordinate
    for the given side 'side'; otherwise returns 'None' if the side is invalid.
    """
    if not _IsSideKnown(side):
        return None
    return side['maxcoord_v']

def GetMaxCoord_Horizontal(side):
    """
    maxcoord_h = Side.GetMaxCoord_Horizontal(side)

    Side.GetMaxCoord_Horizontal returns the maximum horizontal coordinate 
    for the given side $side; otherwise returns 'None' if the side is 
    invalid.
    """
    if not _IsSideKnown(side):
        return None
    return side['maxcoord_h']

def PlaceShip(side, shipName, shipSize, topLeftCoord, orientation):
    """
    placed = Side.PlaceShip(side, shipName, shipSize, topLeftCoord, orientation)

    Side.PlaceShip() places a ship in the given side 'side', with the given 
    ship name 'shipName' (should be unique within the side) and size 
    'shipSize', at the given full coordinate 'topLeftCoord' expressing the 
    ship's top left location, with the given orientation 'orientation' ("h" 
    for horizontal, "v" for vertical); returns true if the ship can be 
    placed, otherwise returns 'None'. 'topLeftCoord' is expected to be formated
    like this: A:3
    """
    if not _IsSideKnown(side):
        return None

    if shipName == 'hit' or shipName == 'free' or shipName == 'missed':
        return None

    if shipName in side['fleet']:
        return None

    coord_v = _GetCoord_Vertical(side, topLeftCoord)
    if not coord_v:
        return None

    coord_h = _GetCoord_Horizontal(side, topLeftCoord)
    if not coord_h:
        return None
    
    orientation = orientation.lower()

    if orientation != 'h' and orientation != 'v':
        return None

    shipCoords = []

    for i in range(int(shipSize)):
        temp_coord = coord_v + ":" + str(coord_h)
        if not temp_coord in side['free']:
            break

        shipCoords.append(temp_coord)

        if orientation == 'h':
            coord_h = int(coord_h) + 1
        elif orientation == 'v':
            coord_v = chr(ord(coord_v) + 1)

    if len(shipCoords) == shipSize:
        side['fleet'][shipName] = {}
        while shipCoords:
            coord = shipCoords.pop(0)
            del side['free'][coord]
            side['fleet'][shipName][coord] = 1
        return True
    else:
        return None

def ShipCount(side):
    """
    shipCount = Side.ShipCount(side)

    Side.ShipCount returns the number of ships still afloat for the given 
    side 'side'; otherwise returns 'None' if the side is invalid.
    """
    if not _IsSideKnown(side):
        return None
    return len(side['fleet'])

def WhatIsAt(side, coord):
    """
    what = Side.WhatIsAt(side, coordinate)

    Side.WhatIsAt returns what is at the given full coordinate 'coordinate'
    for the given side 'side'; a return value of "free" means no ship or 
    shell there, "hit" means a shell hit some ship there, "missed" means a 
    shell missed there, and some other string means a ship by that name is 
    there (and unhit); otherwise returns None if the side or coordinate is 
    invalid.
    """
    if not _IsSideKnown(side):
        return None

    if not _IsCoordValid(side, coord):
        return None

    if coord in side['free']:
        return 'free'
    if coord in side['hit']:
        return 'hit'
    if coord in side['missed']:
        return 'missed'

    for ship in side['fleet']:
        if coord in side['fleet'][ship]:
            return ship

    return None

def FireShell(side, coord):
    """
    result = Side.FireShell(side, coordinate)

    Side.FireShell fires a shell upon the given full coordinate 
    'coordinate' for the given side 'side'; returns what happened there, 
    where "hit" means a ship was already hit there, "hit " followed by a 
    ship name means the shell just hit this ship there, "sunk" followed by 
    a ship name means the shell sunk this ship there, and "missed" means 
    the shell missed there; otherwise returns 'None' if the side or 
    coordinate is invalid.
    """
    if not _IsSideKnown(side):
        return None
    if not _IsCoordValid(side, coord):
        return None

    what = WhatIsAt(side, coord)
    if not what:
        return None

    if what == 'free':
        del side['free'][coord]
        side['missed'][coord] = 1
        return 'missed'
    elif what == 'hit':
        return 'hit'
    elif what == 'missed':
        return 'missed'
    else:
        del side['fleet'][what][coord]
        side['hit'][coord] = 1
        if side['fleet'][what]:
            return "hit " + str(what)
        else:
            del side['fleet'][what]
            return "sunk " + str(what)

        
# INTERNAL UTILITY ROUTINES
# These are not be accessed directly from outside of the Side module.

def _IsSideKnown(side):
    """
    for the given side,
    return True/False whether it was created by NewSide
    """
    if 'ID' in side:
        pass
    else:
        return False

    if not side['ID'] in Sides:
        return False

    return True

def _IsVerticalCoordValid(side, coord_v):
    """
    for the given side,
    return True/False whether 
    the given vertical coordinate is within the proper range;
    will return 'None' if the side is invalid
    """
    if not _IsSideKnown(side):
        return None 

    if 'A' <= coord_v and coord_v <= side['maxcoord_v']:
        return True
    return False

def _IsHorizontalCoordValid(side, coord_h):
    """
    for the given side,
    return True/False whether
    the given horizontal coordinate is within the proper range;
    will return 'None' if the side is invalid
    """
    if not _IsSideKnown(side):
        return None

    if 1 <= int(coord_h) and int(coord_h) <= int(side['maxcoord_h']):
        return True
    return False

def _IsCoordValid(side, coord):
    """
    for the given side,
    return True/False whether
    the given full coordinate is within the proper range;
    will return 'None' if the side is invalid
    """
    if not _IsSideKnown(side):
        return None
    
    if _GetCoord_Vertical(side, coord) and _GetCoord_Horizontal(side, coord):
        return True
    return False

def _GetCoord_Vertical(side, coord):
    """
    For the given side, return the vertical coordinate of the given full
    coordinate if also within the proper range, otherwise return 'None'
    """
    if not _IsSideKnown(side):
        return None

    coord_list = coord.split(":")
    if _IsVerticalCoordValid(side, coord_list[0]):
        return coord_list[0]

    return None

def _GetCoord_Horizontal(side, coord):
    """
    _GetCoord_Horizontal
    For the given side, return the horizontal coordinate of the given full
    coordinate if also within the proper range. Otherwise return 'None'
    """
    if not _IsSideKnown(side):
        return None

    coord_list = coord.split(":")
    if _IsHorizontalCoordValid(side, coord_list[1]):
        return coord_list[1]

    return None

## Main ##
if __name__ == "__main__":
    pass






AND THIS IS MY CODE:

import Side
import random

# ---- APPLICATION ROUTINES

# the ship names and sizes
Ships = {"Patrol Boat": 2,
         "Submarine": 3,
         "Destroyer": 3,
         "Battleship": 4,
         "Carrier": 5
         }

def DrawGrid(side, revealShips):
    """
    for the given side and whether to reveal the ships (true or false),
    draw the grid; 
    dot means no ship or shell there,
    * means a shell hit a ship there,
    + means a shell missed;
    only unhit parts of ships are revealed, using the
    first character in the name
    """
    print "  ",
    
    for i in range(int(Side.GetMaxCoord_Horizontal(side))):
        i = int(i) + 1
        print "%2d " % i,
    print "\n"
    
    for i in range(ord('A'), ord(Side.GetMaxCoord_Vertical(side))+1):
        print chr(i) + " ",
        for j in range(Side.GetMaxCoord_Horizontal(side)):
            temp_coord = chr(i) + ":" + str(j + 1)
            what = Side.WhatIsAt(side, temp_coord)
            
            if what == 'free':
                print " . ",
            elif what == 'hit':
                print " * ",
            elif what == 'missed':
                print " + ",
            elif revealShips:
                print " " + what[0:1] + " ",
            else:
                print " . ",
        print "\n"

def PlaceShipsRandomly(side):

    maxcoord_h = Side.GetMaxCoord_Horizontal(side)

    shipName = 'Carrier'
    shipSize = 5
    rand = random.randint (1,(maxcoord_h))
    rand = str(rand)
    Rand = random.randint (1,(maxcoord_h))
    Rand = chr(Rand + 64)
    topLeftCoord = Rand + ':' + rand
    orientation = random.choice(['v', 'h'])
    if Side.PlaceShip(side, shipName, shipSize, topLeftCoord, orientation) == None:
        Side.PlaceShip(side, shipName, shipSize, topLeftCoord, orientation)

    DrawGrid(side, True)

    
    shipName = 'Battleship'
    shipSize = 4
    rand = random.randint (1,(maxcoord_h))
    rand = str(rand)
    Rand = random.randint (1,(maxcoord_h))
    Rand = chr(Rand + 64)
    topLeftCoord = Rand + ':' + rand
    orientation = random.choice(['v', 'h'])
    Side.PlaceShip(side, shipName, shipSize, topLeftCoord, orientation)
    if Side.PlaceShip(side, shipName, shipSize, topLeftCoord, orientation) == None:
        Side.PlaceShip(side, shipName, shipSize, topLeftCoord, orientation)

    DrawGrid(side, True)

    
    shipName = 'Submarine'
    shipSize = 3
    rand = random.randint (1,(maxcoord_h))
    rand = str(rand)
    Rand = random.randint (1,(maxcoord_h))
    Rand = chr(Rand + 64)
    topLeftCoord = Rand + ':' + rand
    orientation = random.choice(['v', 'h'])
    Side.PlaceShip(side, shipName, shipSize, topLeftCoord, orientation)

    shipName = 'Destroyer'
    shipSize = 3
    rand = random.randint (1,(maxcoord_h))
    rand = str(rand)
    Rand = random.randint (1,(maxcoord_h))
    Rand = chr(Rand + 64)
    topLeftCoord = Rand + ':' + rand
    orientation = random.choice(['v', 'h'])
    Side.PlaceShip(side, shipName, shipSize, topLeftCoord, orientation)

    shipName = 'Patrol Boat'
    shipSize = 2
    rand = random.randint (1,(maxcoord_h))
    rand = str(rand)
    Rand = random.randint (1,(maxcoord_h))
    Rand = chr(Rand + 64)
    topLeftCoord = Rand + ':' + rand
    orientation = random.choice(['v', 'h'])
    Side.PlaceShip(side, shipName, shipSize, topLeftCoord, orientation)

    DrawGrid(side, True)

    
    

def PlaceShipsManually(side):


    print 'Placing Carrier (C) of size 5...'
    shipName = 'Carrier'
    shipSize = 5
    topLeftCoord = raw_input ('Top left location (e.g., b2)?')
    topLeftCoord = str.capitalize(topLeftCoord[0])+ ':' + topLeftCoord[1]
    orientation = raw_input ('(e.g., h or v)?')
    Side.PlaceShip(side, shipName, shipSize, topLeftCoord, orientation)
    ##if Side.PlaceShip(side, shipName, shipSize, topLeftCoord, orientation) == None:
        ##print 'Cannot place ship there. Please try again.'
        ##topLeftCoord = raw_input ('Top left location (e.g., b2)?')
        ##topLeftCoord = str.capitalize(topLeftCoord[0])+ ':' + topLeftCoord[1]
        ##orientation = raw_input ('(e.g., h or v)?')
        ##Side.PlaceShip(side, shipName, shipSize, topLeftCoord, orientation)

    DrawGrid(side, True)
        
    print 'Placing Battleship (B)/> of size 4...'
    shipName = 'Battleship'
    shipSize = 4
    topLeftCoord = raw_input ('Top left location (e.g., b2)?')
    topLeftCoord = str.capitalize(topLeftCoord[0])+ ':' + topLeftCoord[1] + topLeftCoord[2]
    orientation = raw_input ('(e.g., h or v)?')
    Side.PlaceShip(side, shipName, shipSize, topLeftCoord, orientation)
    ##while x == None:
        ##x = Side.PlaceShip(side, shipName, shipSize, topLeftCoord, orientation)
    DrawGrid(side, True)

    print 'Placing Submarine (S) of size 3...'
    shipName = 'Submarine'
    shipSize = 3
    topLeftCoord = raw_input ('Top left location (e.g., b2)?')
    topLeftCoord = str.capitalize(topLeftCoord[0])+ ':' + topLeftCoord[1] + topLeftCoord[2]
    orientation = raw_input ('(e.g., h or v)?')
    Side.PlaceShip(side, shipName, shipSize, topLeftCoord, orientation)
    ##while x == None:
        ##x = Side.PlaceShip(side, shipName, shipSize, topLeftCoord, orientation)
    DrawGrid(side, True)

    print 'Placing Destroyer (D) of size 3...'
    shipName = 'Destroyer'
    shipSize = 3
    topLeftCoord = raw_input ('Top left location (e.g., b2)?')
    topLeftCoord = str.capitalize(topLeftCoord[0])+ ':' + topLeftCoord[1] + topLeftCoord[2]
    orientation = raw_input ('(e.g., h or v)?')
    Side.PlaceShip(side, shipName, shipSize, topLeftCoord, orientation)
    DrawGrid(side, True)

    print 'Placing Patrol Boat (P) of size 2...'
    shipName = 'Patrol Boat'
    shipSize = 2
    topLeftCoord = raw_input ('Top left location (e.g., b2)?')
    topLeftCoord = str.capitalize(topLeftCoord[0])+ ':' + topLeftCoord[1] + topLeftCoord[2]
    orientation = raw_input ('(e.g., h or v)?')
    Side.PlaceShip(side, shipName, shipSize, topLeftCoord, orientation)
    DrawGrid(side, True)

    

    

    """
    for the given side,
    place all their ships by asking where and which orientation
    """
    pass

def PlayOneGame():
    Human_side = Side.NewSide(1, 'J', 10)
    PlaceShipsManually(Human_side)

    ##Computer_side = Side.NewSide(2, 'J', 10)
    ##PlaceShipsRandomly(Computer_side) 

    ##coord = raw_input ('Location to fire upon (e.g., a1)?')
    ##coord = str.capitalize(topLeftCoord[0])+ ':' + topLeftCoord[1] + topLeftCoord[2]
    ##x = FireShell(Computer_side, coord)

    


    """
    initialize and play one full round of the game 
    (until some side's ships are all sunk)
    """
    pass
    
PlayOneGame()


## Application Main ##


Is This A Good Question/Topic? 0
  • +

Replies To: Battleship in Python

#2 Dogstopper  Icon User is offline

  • The Ninjaducky
  • member icon



Reputation: 2874
  • View blog
  • Posts: 11,047
  • Joined: 15-July 08

Re: Battleship in Python

Posted 12 April 2011 - 02:04 AM

Call the WhatIsAt() method. It returns whether a spot is free, hit or contains a ship. From there, you can determine whether you can fire or place there in your set of code.
Was This Post Helpful? 2
  • +
  • -

#3 baavgai  Icon User is offline

  • Dreaming Coder
  • member icon

Reputation: 5874
  • View blog
  • Posts: 12,754
  • Joined: 16-October 07

Re: Battleship in Python

Posted 12 April 2011 - 05:33 AM

Look at the DrawGrid that you wrote. It knows where the hits and misses are.

You can test it out further with some simple code:
side = Side.NewSide(1, 'J', 10)

Side.PlaceShip(side, 'Carrier', 5, 'A:2', 'v')
DrawGrid(side, True)

Side.FireShell(side, 'A:2')
DrawGrid(side, True)

Side.FireShell(side, 'A:1')
DrawGrid(side, True)



Code critique time: OMFG! Side.py is bloody awful. The coordinate system makes unintuitive sound like a compliment. The general layout screams to be made a class, but isn't. Every possible way to use the code is awkward. Module gobals?

We can't fix the module you're working with, but we can fix yours. Your code repeats too much. Let's just look at your PlaceShipsRandomly code. You've defined a global for Ships; use it. Loops are your friends.

e.g.
Ships = {"Patrol Boat": 2,"Submarine": 3,"Destroyer": 3,"Battleship": 4,"Carrier": 5 }

def PlaceShipsRandomly(side):
	def getRandPos(maxcoord_h):
		rand = random.randint (1,(maxcoord_h))
		rand = str(rand)
		Rand = random.randint (1,(maxcoord_h))
		Rand = chr(Rand + 64)
		return (Rand + ':' + rand, random.choice(['v', 'h']))
		
	maxcoord_h = Side.GetMaxCoord_Horizontal(side)
	for shipName, shipSize in Ships.items():
		topLeftCoord, orientation = getRandPos(maxcoord_h)
		while not Side.PlaceShip(side, shipName, shipSize, topLeftCoord, orientation):
			topLeftCoord, orientation = getRandPos(maxcoord_h)
		DrawGrid(side, True)



I've fixed one bug here. Your "if Side.PlaceShip" is redundant and not doing what you want. You want to try to place a ship and if you fail, look somewhere else and try again. The above code does that. It doesn't fix that you're calling a rand on maxcoord_h twice. You have two dimensions, h and v, and you're using the size of one for both.

Part of the confusion is the wonky coordinate system you're forced to deal with. I'd write a bunch of helper functions to get around the given short comings. Indeed, wrapping the horrid code you're been given in your own code might be the sanest thing you can do.

I played with this a little. This is what I came up with.
import Side
import random

Ships = {"Patrol Boat": 2,"Submarine": 3,"Destroyer": 3,"Battleship": 4,"Carrier": 5}

def GetVertLetter(i):
	if i < 1 or i > 25:
		return None
	return chr(ord('A')+i-1)

def GetCoord(vItem, hItem):
	return vItem + ":" + hItem

def GetRandomCoord(side):
	return GetCoord(random.choice(GetVertItems(side)), random.choice(GetHorizItems(side)))


def GetNewSide(rows, cols):
	"""
	side = GetNewSide(rows, cols)
	Both values are between 1..25
	
	The ID thing is sad, and we'll allow for it internally.
	"""
	(ID, maxcoord_v, maxcoord_h) = (len(Side.Sides)+1, GetVertLetter(rows), cols)
	if maxcoord_v:
		side = Side.NewSide(ID, maxcoord_v, maxcoord_h)
		return side
	return None


def GetVertItems(side):
	if not 'vNums' in side:
		len = ord(Side.GetMaxCoord_Vertical(side))-ord('A') + 1
		side['vNums'] = [ GetVertLetter(i+1) for i in range(len) ]
	return side['vNums']


def GetHorizItems(side):
	if not 'hNums' in side:
		side['hNums'] = [ str(i+1) for i in range(Side.GetMaxCoord_Horizontal(side)) ]
	return side['hNums']


def DrawGrid(side, revealShips):
	tokens = {'free': ".", 'hit':"*",'missed':"+" }
	print "  ", " ".join(GetHorizItems(side))
	for vItem in GetVertItems(side):
		print vItem + " ",
		for hItem in GetHorizItems(side):
			what = Side.WhatIsAt(side, GetCoord(vItem, hItem))
			if what in tokens:
				 print tokens[what],
			elif revealShips:
				 print what[0:1],
			else:
				 print ".",
		print

def PlaceShipsRandomly(side):
	for shipName, shipSize in Ships.items():
		isPlaced = False
		while not isPlaced:
			isPlaced = Side.PlaceShip(side, shipName, shipSize, GetRandomCoord(side), random.choice(['v', 'h']))

def FireRandomly(side, n=5):
	for i in range(n):
		what = 'missed'
		while what in ('hit','missed'):
			coord = GetRandomCoord(side)
			what = Side.WhatIsAt(side, coord)
		status = Side.FireShell(side, coord)
		if not status=='missed':
			print status
		

def PlayOneGame():
	side = GetNewSide(12, 10)
	PlaceShipsRandomly(side)
	DrawGrid(side, True)
	FireRandomly(side, 20)
	DrawGrid(side, True)


if __name__ == "__main__":
	PlayOneGame()




Hope that helps.
Was This Post Helpful? 2
  • +
  • -

Page 1 of 1