I am stuck on the parts of the first code that have <> in place of actual code and having trouble getting testing done at the end of the first code, the testing of the functions that I cannot get to work have being quoted out in Python.

from random import randint

class Tank(object):
    """Tank stores all the methods to modify the properties of the tanks"""
    
    def __init__(self, name="", armour=10, firepower=5):
        """Constructor - sets default values for properties"""
        self.x = 0
        self.y = 0
        self.armour = armour
        self.firepower = firepower
        self.name = name

    def __str__(self):
        """Print the tanks properties"""
        return "xpos: " + str(self.x) + " ypos: " + str(self.y) + " armour: " + str(self.armour) + " firepower: " + str(self.firepower)

    def getArmour(self):
        """Return armour value"""
        return self.armour

    def getFirepower(self):
        """Return firepower value"""
        return self.firepower

    def getLocation(self):
        """Return the location as a tuple"""
        return self.x, self.y

    def getName(self):
        """Return name"""
        return self.name

    def getX(self):
        """Return the X coordinate"""
        return self.x

    def getY(self):
        """Return the Y coordinate"""
        return self.y

    def increaseArmour(self, amount):
        """Increase armour value by the amount"""
        self.armour += amount

    def increaseFirepower(self, amount):
        """Increase firepower value by the amount"""
        self.firepower += amount

    def isAt(self, loc):
        """Check if tank is at location loc, return True or False accordingly"""
        return self.getLocation() == loc

    def randomMove(self, gridmax):
        diceRoll = random.randint(1,7)
        directionNumber = random.randrange(1,5)
        directions = {1:"up", 2: "down", 3: "left", 4: "right"}
        theDirection = directions[directionNumber]
        self.move(theDirection, diceRoll, gridmax)
        
    def move(self, direction, amount, gridmax):
        """Moves tanks keeping it inside the grid maximum passed in"""
        if direction == "up":
            <>
            elif direction == "down":
                <>
                elif direction == "left":
                    <>
                    elif direction == "right":
                        <>
                        break

    def reduceArmour(self, amount):
        """Decrease the armour value by the amount"""
        self.armour -= amount
        if self.armour < 1:
            print ":(", self.name, "is gone :("

    def reduceFirepower(self, amount):
        """Decrease the firepower value by the amount"""
        self.firepower -= amount

    def setArmour(self, a):
        """Set armour value"""
        self.armour = a

    def setFirepower(self, f):
        """Set firepower value"""
        self.firepower = f

    def setX(self, x):
        """Set x coordinate"""
        self.x = x

    def setY(self, y):
        """Set y coordinate"""
        self.y = y


#############
## Testing ##
#############

#set Variables for Tank
name = "test"
armour = 20
firepower = 10
amount = 5

print "Testing __init__ .....",
testTank = Tank(name, armour, firepower)
print "Success"

print "Testing __str__ ....."
tankStr = testTank
print "Success"
print "printing tank info: "
print tankStr

print "Testing getArmour .....",
if testTank.getArmour() == armour: #We can test expected outcome
    print "Success"
else:
    print "Failed"

print "Testing getFirepower .....",
if testTank.getFirepower() == firepower:
    print "Success"
else:
    print "Failed"

print "Testing getLocation .....",
if testTank.getLocation() == (0,0):
    print "Success"
else:
    print "Failed"

print "Testing getName .....",
if testTank.getName() == name:
    print "Success"
else:
    print "Failed"

print "Testing getX .....",
if testTank.getX() == 0:
    print "Success"
else:
    print "Failed"

print "Testing getY .....",
if testTank.getY() == 0:
    print "Success"
else:
    print "Failed"

print "Testing increaseArmour .....",
testTank.increaseArmour(amount)
armour += amount #increase expected result
if testTank.getArmour() == armour:
    print "Success"
else:
    print "Failed"

print "Testing increaseFirepower .....",
testTank.increaseFirepower(amount)
firepower += amount #increase expected result
if testTank.getFirepower() == firepower:
    print "Success"
else:
    print "Failed"

print "Testing isAt .....",
if testTank.isAt((0,0)):
    print "Success"
else:
    print "Failed"

print "Testing move .....",
##testTank.move("up", diceroll, gridmax)
##if testTank.move == move:
##    print "Success"
##else:
##    print "Failed"

print "Testing reduceArmour .....",
##if testTank.reduceArmour() == :
##    print "Success"
##else:
##    print "Failed"

print "Testing reduceFirepower .....",
#if testTank.reduceFirepower() == :
 #   print "Success"
#else:
 #   print "Failed:

print "Testing setArmour .....",
##if testTank.setArmour() == armour:
##    print "Success"
##else:
##    print "Failed"

print "Testing setX .....",
##if testTank.setX() == x:
##    print "Success"
##else:
##    print "Failed"

print "Testing setY .....",
##if testTank.setY() == y:
##    print "Success"
##else:
##    print "Failed"
from random import randint


class Grid(object):
    """ Grid is a playing field for tanks. It stores the size and the special squares """

    def __init__(self, maxx = 4, bonusA = 2, bonusF = 4, bonusAmt = 5):
        """ Constructor - set size and special squares """
        self.max = maxx

        # set special squares
        self.bonusSquares = []
        tempLocations = []
        
        # set bonus armour squares
        if bonusA:
            for x in xrange(bonusA):
                loc = randint(-self.max, self.max), randint(-self.max, self.max)
                while loc in tempLocations:
                    loc = randint(-self.max, self.max), randint(-self.max, self.max)
                square = loc, "A", bonusAmt
                self.bonusSquares.append(square)
                tempLocations.append(loc)
                
        # set bonus firepower squares
        if bonusF:
            for x in xrange(bonusF):
                loc = randint(-self.max, self.max), randint(-self.max, self.max)
                while loc in tempLocations:
                    loc = randint(-self.max, self.max), randint(-self.max, self.max)
                square = loc, "F", bonusAmt
                self.bonusSquares.append(square)
                tempLocations.append(loc)

    def __str__(self):
        squaresInfo = ""
        for square in self.bonusSquares:
            squaresInfo += "+" + str(square[2])
            if square[1] == "F":
                squaresInfo += " firepower at "
            else:
                squaresInfo += " armour at "
            squaresInfo += str(square[0]) + ", "
        string = "Maximum dimension: " + str(self.max) + " (width & height are " + str(self.max * 2) \
               + ")\nBonus Squares: " + squaresInfo
        return string[:-2]

    def getMax(self):
        return self.max

    def getSquare(self, loc):
        """ return a single square item given its location """
        if self.getLocation == loc:
            return bonusA, bonusF, bonusAmt

    def landOnBonus(self, loc):
        """ Bonus has been landed on so remove it from list, checked by location """
        for square in self.bonusSquares:
            if square[0] == loc:
                self.bonusSquares.remove(square)

    def getBonusLocations(self):
        """ Return a list of just the locations of the bonus squares """
        locations = []
        for square in self.bonusSquares:
            locations.append(square[0])
        return locations
from grid import Grid
from tank import Tank
import random


t1 = Tank("Meanie", 12, 7)
t2 = Tank("CP1200", 80, 5)
t3 = Tank("Wimpy", 1, 2)
tanks = [t1, t2, t3]


playingDimensions = input("Max dimension for playing field: ")
noBonusArmourSquares = input("Number of bonus armour squares: ")
noBonusFirepowerSquares = input("Number of bonus firepower squares: ")
amountBonus = input("Amount of bonus: ")
widthHeight = playingDimensions * 2 + 1
print "The setup:"
print "Maximum dimension:", playingDimensions, "(width and height are " + str(widthHeight) + ")"
print "Bonus squares:", "+" + str(amountBonus) + " armour at", 
print "+" + str(amountBonus) + " firepower at"
print "Let's ""Play..."""


#Grid.getBonusLocations()

def deadOrAlive(self):
    if self.armour < 1:
        print ":(", self.name, "is gone :("
        tanks.remove(self)

def moveTanks():
    for i in range(len(tanks)):
        diceRoll.append(theDice.roll())
        theDirection.append(theDir.randomDir())
        if theDirection[i] == "up":
            tanks[i].set_y(diceRoll[i])
        elif theDirection[i] == "down":
            tanks[i].set._y(-diceRoll[i])
        elif theDirection[i] == "right":
            tanks[i].set_x(diceRoll[i])
        elif theDirection[i] == "left":
            tanks[i].set_x(-diceRoll[i])

def determineCoords():
    for i in range(len(tanks)):
        if tanks[i].getx() > gridSize:
            tanks[i].x = 2 * gridSize - tanks[i].x
        elif tanks[i].getx() < -gridSize:
            tanks[i].x = -2 * gridSize - tanks[i].x
        elif tanks[i].getY() > gridSize:
            tanks[i].y = 2 * gridSize - tanks[i].y
        elif tanks[i].getY() < - gridSize:
            tanks[i].y = -2 * gridSize - tanks[i].y

def doSpecials():
    for i in range(len(tanks)):
        if tanks[i].getx() == Grid.bonusSquares() and tanks[i].get.y() == Grid.bonusSquares():
            tanks[i].set_a(10)

        if tanks[i].getx() == Grid.bonusSquares() and tanks[i].get.y() == Grid.bonusSquares():
            tanks[i].set.f

def doBattle():
    isBattle = 0
    theCoords = []
    for i in range(len(tanks)):
        theCoords.append([tanks[i].x, tanks[i].y])
        i = 0
        x = 0
        battleLocation = []
        tanksInvolved = []
        for tank in tanks:
            pos = i
            theCoords[pos] = ""
            i += 1
            for coord in theCoords:
                if tank.Coords() == coord:
                    battleLocation.append(tank.Coords())
                    Tank.setArmour(-tanks[x].f)
                    
                x += 1
                if x == len(tanks):
                    x = 0
                    theCoords[pos] = tank.Coords()
                    break

        #Remove duplicates from battle location list
        fighting = []
        while battleLocation != []:
            item = battleLocation.pop(0)
            if item not in fighting:
                fighting.append(item)
        for cord in fighting:
            print "- Battle! -", self.name, self.name, "are fighting"
        return isBattle


def gameOver():
    if len(tanks) <= 1:
        print "After", rounds, "rounds, and", noBattles, "battles, the winner is...\n", tanks

def main():

    noBattles = 0
    #diceRoll = Die()
    #theDirections = Direction()

    while len(tanks) >= 2:
        moveTanks()
        determineCoords()
        doSpecials()
        doBattle()
        battle = theBattle()
        noBattles += 1
        deadCheck()
        gameOver()

gridSize = playingDimensions

main()

Dani AI

Generated

A compact, practical checklist that ties together the useful points already made by and and fills gaps left by the posted code and tests.

The core problems seen in the thread are: inconsistent use of the random API (mixing from random import ... with random.xxx), incorrectly indented/structured branches inside the move method (and a stray break), boundary-handling left unspecified (clamp, wrap, or reflect), and fragile test code that prints pass/fail instead of using assertions or a test framework. ’s boundary suggestion is a solid fix; is correct that direct attribute access is simpler in Python and suits quick tests.

Concrete fixes and patterns to apply

  • Make random imports consistent: either import random and call random.randint(...), or import specific functions and call them directly.
  • Replace the broken branch structure in move with four aligned branches (up/down/left/right) and a clear boundary policy (clamp or reflect). A tiny helper makes clamping easy:
def clamp(v, lo, hi):
    return max(lo, min(v, hi))

# example use (in main movement logic)
tank.y = clamp(tank.y + dy, -grid_max, grid_max)
  • For bonus squares, have Grid.search iterate self.bonusSquares and return the matching tuple when square[0] == loc (avoid comparing to a non-existent getLocation method). When removing items, avoid mutating a list while iterating — filter or break immediately after removal.

Suggested game-loop structure (keeps Tank simple)

  • Keep Tank as a small data object (x,y,armour,firepower) and run movement, boundary correction, specials and battles from the main loop or a Grid controller. To detect battles, group tanks by position and operate on each position’s occupants:
from collections import defaultdict
by_pos = defaultdict(list)
for t in tanks:
    by_pos[(t.x, t.y)].append(t)

for pos, occ in by_pos.items():
    if len(occ) > 1:
        for t in occ:
            t.armour -= sum(o.firepower for o in occ if o is not t)

Testing recommendations

  • Replace print-based checks with assert or unittest tests so failures are explicit and automatable. Example with unittest:
import unittest
class TestTank(unittest.TestCase):
    def test_reduce_armour(self):
        t = Tank('A', armour=10, firepower=1)
        t.reduceArmour(4)
        self.assertEqual(t.armour, 6)

Finally, be mindful of Python version differences (in modern Python use print() and range()); converting to a small unittest suite will make the movement/battle fixes obvious when they work.

Recommended Answers

All 9 Replies

at line 64, I suspect you want

self.y -= amount
if self.y < gridMin:
  self.y = gridMin

and similar things in the other blocks. I had to make up gridMin. If I were doing this, I might have a tank constructed on a grid (the grid instance in Tank's constructor) and would then not need to pass in gridMax etc: if self.y < self.grid.minY: ... You also need to deal with the incorrect indentation in the vicinity.

Thanks for that griswolf, after some thinking I figured it would be better to have movement in the main file instead of part of the tanks class. So what would I need to make it work in main instead of tanks?

Why do you have so many getter and setter methods? You don't need them, so I recommend removing them. Python has no need for that kind of getters and setters.

To add increase or reduce armor/firepower/location. So how would I do it without using the getters and setters I am currently using?

You need to think about whether a grid "has" one or more tanks (in it) or each tank "has" a grid (it is on). It is easier at this level to let the tanks know about the grid since there is only the one grid, but if you have the grid know about the tanks, you need a list (or some container) of tanks. ... however...

I presume you are going to do some kind of collision detection, or add a Cannonball class or some such thing eventually. In that case "some entity" needs to have a list of the (lets call them sprites) on the grid. You can do that by having the sprite register with the entity. The registration might involve a simple list in the grid, or (better, for reasons that will become clear later) it might be a message queue type thing where any sprite (or the grid) can send a message to any (selectable set of) sprite(s). One of the messages is "Where are you?" and there is some fun in store trying to decide how best to detect collisions. The naive algorithm takes N factorial comparisons (but there are only width * height locations on the grid, so maybe locations should see if they have more than one sprite on board)

The 'later' is that with a message hub, tanks can talk to each other, a nice effect.

To add increase or reduce armor/firepower/location. So how would I do it without using the getters and setters I am currently using?

tank1.armour -= 10  #decrease this tanks armour by 10
tank1.armour  #instead of tank1.getArmour() or something like that

in Python, everything is accessible and you just get/set them like that

armour is decreased by another tank's firepower when they are on the same square, so they each attack once then move on.

griswolf, what you suggested is working with some additions to my code. Just need to get the testing side of it working, thanks for the help.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.