Hey guys. I originally wrote this program as a University assignment. It's basically a simulated game of 'tanks' roaming around a grid blowing eachother up. It isn't intended to be the most exciting game in the universe, infact when you look at the programs' output there will just be a whole heap of numbers that represent where the tanks are located on a 'grid', the tanks armour values, firepower values, etc, etc. I probably should have created a nice little GUI to go with it... but I lack time at the moment.
However, I thought I might as well post it on here. I've had a look through the code snippets and haven't found anything remotely simular to this. I believe it might give people who are beginning to delve in to the world of Object Orientated Programming a working example of how to go about it. It also illustrates how lists can be used to store object instances.
Keep in mind that I'm not very far advanced in terms of Python Programming - I've only done it for one semester at Uni. Any suggestions/feedback you can provide would be greatly appreciated.

import random, sys

#################
#----CLASSES----#
#################

# this is the dice class. It tells the tanks how many spaces to move
class Die(object):

def roll(self):
diceRoll=random.randrange(1,7)
return diceRoll

# this is the directon class. It tells the tank which direction to move.
class Direction(object):

def randomDir(self):
directionNumber=random.randrange(1,5)
directions={1:"up", 2:"down", 3:"left", 4:"right"}
theDirection=directions[directionNumber]
return theDirection

# This is the class that creates a tank. When called, an instance of Tank is
# created with the attributes defined in the __init__ method.
class Tank(object):

def __init__(self, tankNo, xPos = 0, yPos = 0, Armour = 10, Firepower = 1, Name=""):
self.tankNo=tankNo
self.xPos = xPos
self.yPos = yPos
self.armour = Armour
self.firepower = Firepower
self.name = raw_input(">> ")
if self.name=="":
self.name = "Tank " + str(tankNo)

# The tanks getter methods, These simply return self attribute values
def get_XPos(self):
return self.xPos

def get_YPos(self):
return self.yPos

def get_Armour(self):
return self.armour

def get_Firepower(self):
return self.firepower

def get_Name(self):
return self.name

# The tanks setter methods. These are used to modify self attribute values.
def set_XPos(self, leftRight):
self.xPos += leftRight

def set_YPos(self, upDown):
self.yPos += upDown

def set_Armour(self, nArmour):
self.armour += nArmour

def set_Firepower(self, nFirepower):
self.firepower += nFirepower

# This method checks to see if self has had its' armour attribute reduced
# below the value of 0. If it has, it removes itself from the theTanks
# list, thus removing itself from the game.
if self.armour < 0:
print "\n\t"+"+"*45
print "\t ",self.name, "has died at coordinates: x-", self.get_XPos(), "y-", self.get_YPos()
theTanks.remove(self)
print "\t"+"+"*45

# This method simply returns the xPos and yPos of the tank in the form of a list
def Coords(self):
return [self.xPos, self.yPos]

#################
#---FUNCTIONS---#
#################

# This is the menu function. It's functionality is limited to just that, it
# gives the user options of what they can do with the program. There's some
# error checking bits and pieces in here too.
print """
------------------------
B A T T L E  T A N K S
------------------------
By Scott Hayward

(1) Game Information
(2) Start Game
(3) Exit"""
if menuChoice not in ("1", "2","3"):
instructions()
main()
raw_input("\n\t\t\tPush enter to exit")
sys.exit()

def tankNumbers():
# This is where the user specifies the number of tanks they want in the
# battle. There can't be any more than 9 tanks and no less than 2.
global numberOfTanks
validNo=False
while validNo==False:
try:
numberOfTanks = raw_input("How many tanks in the battle : ")
numberOfTanks=int(numberOfTanks)
if numberOfTanks > 9:
print "\n\tNo more than 9 tanks are allowed in the battle.\n"
elif numberOfTanks < 2:
print "\n\tThere needs to be more than one tank in the battle.\n"
else:
validNo=True
except ValueError:
if numberOfTanks=="":
numberOfTanks=2
validNo=True
else:

def createTanks():
# This function creates the 'tanks'. The number of tanks that are created
# depends on the value stored in the numberOfTanks variable. The instances
# of Tank are stored in the theTanks list.
global theTanks
theTanks=[]
for i in range(numberOfTanks):
tankNumber = i+1
print "\nPlease enter Tank " + str(tankNumber) + "'s name..."
theTanks.append(Tank(tankNo=tankNumber))

def moveTanks():
# This function moves the tanks around the 'grid'. The direction and number
# if spaces they more are determined by the Die and Direction classes.
global theDies
global theDirections
theDies=[]
theDirections=[]
for i in range(len(theTanks)):
theDies.append(theDice.roll())
theDirections.append(theDir.randomDir())
if theDirections[i] =="up":
theTanks[i].set_YPos(theDies[i])
elif theDirections[i]=="down":
theTanks[i].set_YPos(-theDies[i])
elif theDirections[i] =="right":
theTanks[i].set_XPos(theDies[i])
elif theDirections[i]=="left":
theTanks[i].set_XPos(-theDies[i])

def determineCoords():
# This function stops the tanks from moving outside of the 'grid'. If a tank
# moves to gridSize or -gridSize, it will bounce back in the opposite
# direction. For example: if a tank is at x:9, y:0 and is told to go right
# 5 spaces, it will go to 10 (gridSize), then bounce back to the left 4
# spaces landing on x:6 y:0.
for i in range(len(theTanks)):
if theTanks[i].get_XPos() > gridSize:
theTanks[i].xPos = 2 * gridSize - theTanks[i].xPos
print "\n\t***", theTanks[i].name, "- B O U N C E ***"
elif theTanks[i].get_XPos() < -gridSize:
theTanks[i].xPos = -2 * gridSize - theTanks[i].xPos
print "\n\t***", theTanks[i].name, "- B O U N C E ***"
elif theTanks[i].get_YPos() > gridSize:
theTanks[i].yPos = 2 * gridSize - theTanks[i].yPos
print "\n\t***", theTanks[i].name, "- B O U N C E ***"
elif theTanks[i].get_YPos() < -gridSize:
theTanks[i].yPos = -2 * gridSize - theTanks[i].yPos
print "\n\t***", theTanks[i].name, "- B O U N C E ***"

def doSpecials():
#If the tanks land on -3,-3 one is added to the firepower attribute of the tank
#If the tanks land on 3,3 ten is added to the armour attribute of the tank
specialSquare = 3
for i in range(len(theTanks)):
if theTanks[i].get_XPos() == specialSquare and theTanks[i].get_YPos() == specialSquare:
print "\n\t+++", theTanks[i].name, "- Yipee - More Armour +++"
theTanks[i].set_Armour(10)

if theTanks[i].get_XPos() == -specialSquare and theTanks[i].get_YPos() == -specialSquare:
print "\n\t+++", theTanks[i].name, "- yipee - More Firepower +++"
theTanks[i].set_Firepower(1)

def theBattle():
# Probably a much easier way to achieve what I was after but this works
# so it'll do! Basically a loop iterates through a list of object instances
# (theTanks). Another loop is used to iterate through a list of coordinates
# that the tanks are at. If the coordinates of tank are equal
# to coord, tank's armour is reduced by the tank which coord is related to's
# firepower.
isBattle=0
theCoords=[]
for i in range(len(theTanks)):
theCoords.append([theTanks[i].xPos, theTanks[i].yPos])
i=0
x=0
battleLocation=[]
tanksInvolved=[]
for tank in theTanks:
pos=i
theCoords[pos]=""
i+=1
for coord in theCoords:
if tank.Coords()==coord:
battleLocation.append(tank.Coords())
tank.set_Armour(-theTanks[x].firepower)
isBattle=1

x+=1
if x ==len(theTanks):
x=0
theCoords[pos]=tank.Coords()
break
# Remove duplicates from battleLocation list
fighting = []
while battleLocation != []:
item = battleLocation.pop(0)
if item not in fighting:
fighting.append(item)
for cord in fighting:
print "\n\t"+"#"*45
print "\t\tB A T T L E  @  X:",cord[0],"- Y:",cord[1]
print "\t"+"#"*45
return isBattle

# Checks to see if any of the tanks are dead or not. It calls the
# deadOrAlive method in the Tank class to get this information.
for i in range(len(theTanks)):
for tank in theTanks:

def gameOver():
# This function checks to see if the game is over or not.
if len(theTanks)==0:
print "\n\n\n\t\t"+"#"*45
print "\t\t\t B A T T L E  R E S U L T S"
print "\t\t"+"#"*45
print "\n\tThe tanks killed eachother at the same time. Unfortunate that..."
elif len(theTanks)==1:
print "\n\n\n\t\t"+"#"*45
print "\t\t\t B A T T L E  R E S U L T S"
print "\t\t"+"#"*45
print "\n\t\t",theTanks[0].name, "is the winner! Congratulations", theTanks[0].name

def instructions():
# Just instructions
print "\nThis game was originally written as a University assignment. The original \nversion only had two tanks. However after writing and submitting the assignment I was compelled to modify the program to allow for a user-defined number of \ntanks. In doing so it has aided my knowledge and understanding of Object \nOrientated Programming to the extent that I believe it may prove to be a good \nexample to others that are just being introduced to the world of OOP."
print "\nAlbeit not a very 'exciting' program, it illustrates how 'lists' can be used to hold class object instances. It also illustrates the effective use of class \ngetter and setter methods."
print "\nThe basic concept of the game is to have a user-defined number of 'tanks' \nroaming around a grid that goes from 10 to -10 on the X axis and 10 to -10 \non the Y axis. Each tank starts with the following attributes: X and \nY coordinates of 0 (so each tank starts the battle on 0,0), an Armour attribute of 10 and a Firepower attribute of 1."
print "\nThe tanks move around the grid by responding to randomly generated numbers that indicate the amount of spaces to move, in which direction. There are two special squares on the grid; one is located at coordinates 3,3 which adds 10 to the \ntanks Armour attribute when landed on. The other is located at coordinates -3, \n-3 which adds 1 to the tanks firepower attribute when landed on."
print "\nWhen two or more tanks land on the same square a battle occurs. Each tank's \nArmour attribute is reduced by the other tank's firepower attribute. The game \ncontinues until there is only one tank left in the game. Or until there are no \ntanks left in the game (this indicates a draw, it's pretty rare for this to \nhappen)."
print "\nAt the beginning of the game the user will be asked to enter the number of \ntanks in the battle. I have limitted this to 9, simply because having 10 or \nmore tanks in the game screws up the data tables and it looks stupid."
raw_input("\nWell that's about it! Please note that this game wasn't supposed to be the most enjoyable game ever created. It was simply made to improve my knowledge and \nunderstanding of OOP. I hope it helps anyone else that is looking to dive in to the world of OOP.\n\nPush enter to return to the menu.")

def main():
counter=0
global theDice
global theDir
theDice=Die()
theDir=Direction()
print "\n"+"-"*73
tankNumbers()
createTanks()
while len(theTanks) >= 2:
print "\n" + "-" * 73
print "        Die", "\tDir", "\t\txPos", "\tyPos", "\t\tArmour", "\tFirepower"
print "-" * 73
moveTanks()
determineCoords()
doSpecials()
battle=theBattle()
counter+=1
for i in range(len(theTanks)):
print "\n", theTanks[i].name, "\t",theDies[i], "\t", theDirections[i], "\t\t", theTanks[i].get_XPos(), "\t", theTanks[i].get_YPos(), "\t\t", theTanks[i].get_Armour(), "\t", theTanks[i].get_Firepower(), "\n"
if battle==1:
raw_input()
gameOver()
print "\t\t\t   Number of moves: ", counter
del theDies[:]
del theDirections[:]
del theTanks[:]
if playAgain=="again":
main()
else:

# This variable determines the size of the 'grid'. For example, with the default
# value of 10, the grid goes from 10 to -10 on both X and Y axis.
gridSize=10

if __name__=="__main__":

showMenu()
2
Contributors
1
2
Views
8 Years
Discussion Span
Last Post by Murtan

Generally, I like it. (You're right, a GUI would help a lot to follow what's going on. )

Just a couple of nit-picky items:

I don't think it is good form to have the Tank's __init__ method prompt the user for a tank name. I'd rather see the prompt in createTanks() and have it passed to the Tank's __init__

I have a small issue with the tank's setter methods, they don't 'SET' they modify, making the name misleading. It works fine for this application, but from the name, I would have expected set_XPos(5) to set the Tank's X coordinate to 5, and not for it to move the tank 5 positions to the right.

Have something to contribute to this discussion? Please be thoughtful, detailed and courteous, and be sure to adhere to our posting rules.