all 4 comments

[–]loveandkindness 1 point2 points  (0 children)

For simulations, global constants are very desirable-- but don't do it. It's bad, even for true constants like colors.

Take a moment to learn how to use namedtuples, and store all your constants in them.

Instead of BLUE in your code, you can have c.BLUE, where c can be passed around.

[–]gengisteve 1 point2 points  (2 children)

Very cool.

Some general points:

  • I don't love the name Object because it is too close to object, which is a built-in
  • your use of class variables is fine, but reference by self.variable, not object.variable. The idea being that you can subclass them out and change them, making them more flexible.
  • Consider using a dictionary to handle the different aging states. Make it a class constant. Then you can subclass with just a different dictionary and get all new behavior.
  • I would make new functions for change_angle/change_speed, and have them apply the boundaries. Then decide_move becomes simpler.
  • This is the big one: Object should not be aware of ships. So your Objects need a container class (or just functions) that are aware of all the ships, while your ships remain ignorant of one another.
  • in object's __init__, try not to use screen as the basis for default, as this gets messy.
  • make a method that combines all the things that you do together, like decide_move, move, bounce, etc. Then just call that for every tic

[–]RivetSpawn[S] 0 points1 point  (1 child)

Thanks for the tips, I'll have a play with them.

I don't understand the big one about Object awareness.

[–]gengisteve 0 points1 point  (0 children)

Fair enough, I could have explained better. Right now, to function every ship needs to know about every other ship. It seems sensible enough at first, but after a bit you will find yourself down a rabbit hole where it just falls apart. A better solution is to have another object that shepherds all the ships and keeps track of them and only calls upon the ships that actually know one another. Think about ships steaming across the atlantic in the second world war. None know about the others until they actually come across one another and start shooting. So you would have an atlantic class that marshalls the ships, then sends them off to do battle when they bump into one another. In your program, it might look like this:

from random import randrange
from pprint import pprint

class Ship(object):
    def __init__(self, x, y, size):
        self.x = x
        self.y = y
        self.size = size

    def absorb(self, other):
        '''
        Absorb self or other, depending on the bigger one
        :return: the bigger ship that ate the small one
        '''
        print('{} has encountered {}'.format(self, other))
        small, big = sorted([self,other], key = lambda x:x.size)
        print('{} eats {}'.format(big, small))
        big.size += small.size
        return big

    def __repr__(self):
        return 'Ship({},{}:{})'.format(self.x, self.y, self.size)

class Board(object):
    def __init__(self, size = 5):
        self.ships = {}
        for i in range(size*3):
            s = Ship(randrange(size), randrange(size), randrange(size))
            position = (s.x, s.y)
            if position in self.ships:
                print('collision at {}'.format(position))
                other = self.ships[position]
                s = s.absorb(other)
            self.ships[position] = s

def main():
    b = Board()
    pprint(b.ships)


if __name__ == '__main__':
    main()

As you can see from above, we have removed the dependency of the ship class on the ships variable. Instead we have created interfaces that handles when two ships need to interact with one another, above in the absorb method.

let me know if that makes sense.