469,902 Members | 1,978 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 469,902 developers. It's quick & easy.

Need ideas and insights on a project...

true911m
I would like to solicit your thoughts and ideas (code can come later) about how you would approach a project idea I just tossed together. I chose this as a testbed for me to play with objects, specifically multiple instances of a few specific objects, and as I thought about it, it raised a lot of questions about how to manage, contain, signal all of those objects that do almost the same thing at the same time.

I want to build an OO bingo simulator. While it is not necessarily the most efficient approach, let's academically assume it needs to be object-oriented.

We have 75 ball objects, which can be picked randomly by a machine, and which therefore need an awareness of being picked(), since no ball can come up twice in a game.

We have a scorecard grid with 25 square objects on it, and appropriate values for each column of the grid (B: 1-15,i:16-30, etc.), which are chosen randomly on each card. If we work with all 75 possible numbers (maybe not the best approach), each number needs to know if it's on the bingo card for this game, whether it's matched a number that's already been picked(), and whether it's a Freebie (middle square on the card - 'FREE' instead of a number)

Initial questions:

Can like objects be grouped by some mechanism such that changing an external parameter has them respond to that change, or do they each need to be iterated/manipulated each time to become "aware" of global changes? For example, a number is picked() - can the squares "know" if one matches the new number, or do I need to trigger each one to check?

Maybe I'm thinking in more of a parallel-thread mode here, one thread per object, which remains "active" during the game. Is that way out there? It's probably over my head at this point anyway.

Reel me in; tell me what you think.
Dec 16 '06 #1
27 1853
bartonc
6,596 Expert 4TB
We have 75 ball objects, which can be picked randomly by a machine, and which therefore need an awareness of being picked(), since no ball can come up twice in a game.
Actually, I believe that the machine is the object in question and that balls are data that it dispences. The machine (object) has a better chance of encapsulating awareness of which numbers have been dispensed than a ball does.

We have a scorecard grid with 25 square objects on it, and appropriate values for each column of the grid (B: 1-15,i:16-30, etc.), which are chosen randomly on each card. If we work with all 75 possible numbers (maybe not the best approach), each number needs to know if it's on the bingo card for this game, whether it's matched a number that's already been picked(), and whether it's a Freebie (middle square on the card - 'FREE' instead of a number)

Initial questions:

Can like objects be grouped by some mechanism such that changing an external parameter has them respond to that change, or do they each need to be iterated/manipulated each time to become "aware" of global changes? For example, a number is picked() - can the squares "know" if one matches the new number, or do I need to trigger each one to check?
I've got similar thoughts here. Going to look for some examples

Maybe I'm thinking in more of a parallel-thread mode here, one thread per object, which remains "active" during the game. Is that way out there? It's probably over my head at this point anyway.

Reel me in; tell me what you think.
Do you have Robin Dunn's book: wxPython in Action?
Dec 16 '06 #2
Actually, I believe that the machine is the object in question and that balls are data that it dispences. The machine (object) has a better chance of encapsulating awareness of which numbers have been dispensed than a ball does.



I've got similar thoughts here. Going to look for some examples



Do you have Robin Dunn's book: wxPython in Action?
I haven't read it all, just looked up a few things.

I'm not really interested in the GUI in particular, more the logic. If the GUI objects are easier to build/attach to than abstract code items, that's fine.
Dec 16 '06 #3
The reason for the balls, instead of the machine, as objects is that I want to exercise multiple identical objects in action. JFYI.
Dec 16 '06 #4
bartonc
6,596 Expert 4TB
The reason for the balls, instead of the machine, as objects is that I want to exercise multiple identical objects in action. JFYI.
Ok, I'll buy that. So if each ball instance needs to know which number was just picked() here's how I'd (almost) lay out the structure:

Expand|Select|Wrap|Line Numbers
  1. # I do this a bit differently, but Mr. Dunn says something like this:
  2.  
  3. class NumberDispenser(object):
  4.     def __init__(self):
  5.         self.clients = []
  6.  
  7.     def RegisterClient(self, client):
  8.         self.clients.append(client)
  9.  
  10.     def UpdateClients(self, data):
  11.         for client in self.clients:
  12.             client.Update(data)     # This part I don't like:
  13.                                     # having to know the name of the client function here
  14.  
  15. class NumberClient(object):
  16.     def __init__(self, server, name):
  17.         self.name = name
  18.         server.RegisterClient(self)
  19.  
  20.     def Update(self, data):
  21.         print self.name, data
  22.  
  23.  
  24. if __name__ == "__main__":
  25.     server = NumberDispenser()
  26.     client1 = NumberClient(server, "client #1")
  27.     client2 = NumberClient(server, "client #2")
  28.  
  29.     # call this in response to some event or whatever
  30.     server.UpdateClients(6545)
Dec 16 '06 #5
bartonc
6,596 Expert 4TB
I haven't read it all, just looked up a few things.

I'm not really interested in the GUI in particular, more the logic. If the GUI objects are easier to build/attach to than abstract code items, that's fine.
Actually, I don't use the GUI stuff from the book. I really like (and use similar design) the Model-View-Controler idea. There is also threading discussion toward the end.
Dec 16 '06 #6
Actually, I don't use the GUI stuff from the book. I really like (and use similar design) the Model-View-Controler idea. There is also threading discussion toward the end.
OK, that's not ringing any bells. Can you elaborate on this - what, where, refs?
Dec 16 '06 #7
Ok, I'll buy that. So if each ball instance needs to know which number was just picked() here's how I'd (almost) lay out the structure:

Expand|Select|Wrap|Line Numbers
  1.     def UpdateClients(self, data):
  2.         for client in self.clients:
  3.             client.Update(data)     # This part I don't like:
  4.                                     # having to know the name of the client function here
  5.  
  6. ...
  7. if __name__ == "__main__":
  8.     server = NumberDispenser()
  9.     client1 = NumberClient(server, "client #1")  # <===
  10.     client2 = NumberClient(server, "client #2")  # <===
  11.  



Regarding that, my original thought, which is admittedly BASIC-esque, was to define an array where each element would become an instance. I don't know how to translate that here, but such a construct would alleviate much of the naming overhead. I guess it would also be redundant, since if I did that I wouldn't need the objects at all. :) But like I said, it's an academic exercise...

Can I use some form of eval() to create something like (this is just off the top of my head, trying to get the idea across):
Expand|Select|Wrap|Line Numbers
  1. varhdr = 'client'
  2.  
  3. for i in range(75):
  4.     eval(varhdr+str(i)+' = NumberClient(server, "client #'+str(i)+'")')
  5.  
Don't know if that's close to a proper use of eval; in SB we had a means of constructing dynamic lines of code like that so they could be reused, as in a loop.

This problem is what I was referring to when I mentioned a curiousity about how people would "group"control of these objects.

Actually, the use of a class variable, as discussed in that other thread, may make sense here. Such as, when a number is picked,

Expand|Select|Wrap|Line Numbers
  1. NumberClient.LastPicked=32
  2.  
  3. for square in grid:
  4.     # execute instance.CheckPicked code here
  5.     # each instance of NumberClient class now has 
  6.     # a self.LastPicked value of 32 to process against
  7.  
  8.  
Dec 16 '06 #8
bartonc
6,596 Expert 4TB
Regarding that, my original thought, which is admittedly BASIC-esque, was to define an array where each element would become an instance. I don't know how to translate that here, but such a construct would alleviate much of the naming overhead. I guess it would also be redundant, since if I did that I wouldn't need the objects at all. :) But like I said, it's an academic exercise...

Can I use some form of eval() to create something like (this is just off the top of my head, trying to get the idea across):
Expand|Select|Wrap|Line Numbers
  1. varhdr = 'client'
  2.  
  3. for i in range(75):
  4.     eval(varhdr+str(i)+' = NumberClient(server, "client #'+str(i)+'")')
  5.  
Don't know if that's close to a proper use of eval; in SB we had a means of constructing dynamic lines of code like that so they could be reused, as in a loop.

This problem is what I was referring to when I mentioned a curiousity about how people would "group"control of these objects.

Actually, the use of a class variable, as discussed in that other thread, may make sense here. Such as, when a number is picked,

Expand|Select|Wrap|Line Numbers
  1. NumberClient.LastPicked=32
  2.  
  3. for square in grid:
  4.     # execute instance.CheckPicked code here
  5.     # each instance of NumberClient class now has 
  6.     # a self.LastPicked value of 32 to process against
  7.  
  8.  
A list comprehension is what you are looking for. A comprehension is a very handy tool when you don't need much stuff going on in a loop, but you do need elements appended to a list. This technique creates the list object and fills it in all at once. Notice, also, the C-line string formatting. I use it all the time.


Expand|Select|Wrap|Line Numbers
  1. class NumberDispenser(object):
  2.     def __init__(self):
  3.         self.clients = []
  4.  
  5.     def RegisterClient(self, client):
  6.         self.clients.append(client)
  7.  
  8.     def UpdateClients(self, data):
  9.         for client in self.clients:
  10.             client.Update(data)     # This part I don't like:
  11.                                     # having to know the name of the client function here
  12.  
  13. class NumberClient(object):
  14.     def __init__(self, server, name):
  15.         self.name = name
  16.         server.RegisterClient(self)
  17.  
  18.     def Update(self, data):
  19.         print self.name, data
  20.  
  21.  
  22. if __name__ == "__main__":
  23.     server = NumberDispenser()
  24.     # list comprehension syntax uses [] on around the expession, generators use ()
  25.     instanceList = [NumberClient(server, "client%d" %i) for i in range(5)]
  26.     for inst in instanceList:
  27.         print inst.name
Dec 17 '06 #9
bartonc
6,596 Expert 4TB
The list comprehension here
Expand|Select|Wrap|Line Numbers
  1. instanceList = [NumberClient(server, "client%d" %i) for i in range(5)]
is equivalent to
Expand|Select|Wrap|Line Numbers
  1. instanceList = []
  2. for i in range(5):
  3.     instanceList.append(NumberClient(server, "client%d" %i))
which may be easier to read. I learned comprehensions so long ago that I have forgotten if the benefits actually outweigh the mashed syntax. If I get a break, I'll look it up and get back to you.
Dec 17 '06 #10
The list comprehension here
Expand|Select|Wrap|Line Numbers
  1. instanceList = [NumberClient(server, "client%d" %i) for i in range(5)]
is equivalent to
Expand|Select|Wrap|Line Numbers
  1. instanceList = []
  2. for i in range(5):
  3.     instanceList.append(NumberClient(server, "client%d" %i))
which may be easier to read. I learned comprehensions so long ago that I have forgotten if the benefits actually outweigh the mashed syntax. If I get a break, I'll look it up and get back to you.
Not sure what you're looking up, but I get the listcomp. That does what I was thinking; it didn't occur to me to use the inline string formatting to achieve the desired result.
Dec 17 '06 #11
bvdet
2,851 Expert Mod 2GB
I have a couple of simple thoughts on your Bingo project:
The balls could be a list.
Expand|Select|Wrap|Line Numbers
  1. x = random.sample(range(1, 26, 1),25)
  2. >>> x
  3. [17, 23, 12, 18, 9, 6, 13, 10, 24, 7, 15, 3, 22, 4, 11, 8, 2, 14, 20, 25, 1, 19, 16, 5, 21]
When it is time to select a ball, pop it from the list.

The 'game' could be a dictionary of lists. The dictionary keys are 'card1', 'card2',...... and each list would be filled randomly with unique numbers. When a number is selected, each player's card is scanned for a match, and if a match is found, the list position is set to False.
Expand|Select|Wrap|Line Numbers
  1. for card in dd.keys():
  2.     if ball in dd[card]:
  3.         dd['card1'][dd.['card1'].index(ball)] == False
After a match is found, a card scan method could be called to check for Bingo.

My contribution to this tread is probably be limited by my experience. I try to keep things simple. Most of the applications I have developed involve calculations in 3D space, manipulation of lists, dictionaries, tuples and strings, and reading/writing small data files. Classes incorporate instance variables and functions and sometimes methods when useful. Typically I do not modify instance data externally. I have a Python project coming up - add a steel angle brace between two structural steel beams. At the right time I'll start a thread on it.

Please keep this thead going - I want to know how it turns out.

Best Regards,
BV

ps - tru911m (Mark), Are you from Baltimore, MD? I detailed the structural steel for the Hippodrome PAC on N. Eutah a few years ago. Beautiful structure.
Dec 17 '06 #12
Hi BV,

Yes, I'm a lifelong Baltimoron as we jokingly say. I remember the Hippodrome from its declining days as a moviehouse back in the 70s. It's great that that area is receiving some deserved attention to (at lease some of ) the irreplaceable structures there. It used to be the grand shopping mecca downtown, before malls and the burbs took over.

As for the bingo, I'm pretty straightforward myself. In fact I keep slipping off the path I've laid for myself, in that this project has the arbitrary requirement that these items use objects to accomplish the task, even though it can be directly handled without them.

I'm trying, through use, to exercise my understanding of the interactions and management of simple objects, and I really want to create a couple of classes (balls in machine, squares on the bingo card) that must be instantiated many times (75 numbers, 25 squares) to get the hang of dealing with them all.

In fact, one misconception I keep falling back to is that an 'object' is more like a 'bot' - as if each of these items takes up CPU on its own in a separate thread, and all I have to do is 'talk' to them like IRC bots or something. In fact, I know better, that I have to go 'process' each one based on some sort of event, but that's what I keep seeing in my head, and I'm trying to shake it.
Dec 17 '06 #13
OK, getting a really basic start here. I apologize if any of this repeats -- until it clicks, I'm having some issues retaining a few things.

Based on Barton's tutorial on variables in classes, I just received an unexpected result. Here's a sample:

Expand|Select|Wrap|Line Numbers
  1. class BingoCard:
  2.     def __init__(self):
  3.         self.card=[]
  4.     def FillGrid(self):
  5.         for row in range(5):
  6.             for col in range(5):
  7.                 self.card.append((row,col))  # replace tuples with objects
  8.     def ShowGrid(self):
  9.         print self.card
  10.  
  11. card=BingoCard()
  12. card.FillGrid()
  13. card.ShowGrid()
  14. print card.card
I expected to need ShowGrid() to get at my generated bingo card. What I didn't expect is that the last line, print, will also display the grid.

Since self.card is defined in BingoCard.__init__, I thought it would be inaccessible from the outside world, but the output shows differently:

Expand|Select|Wrap|Line Numbers
  1. [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (3, 0), (3, 1), (3, 2), (3, 3), (3, 4), (4, 0), (4, 1), (4, 2), (4, 3), (4, 4)]
  2. [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (3, 0), (3, 1), (3, 2), (3, 3), (3, 4), (4, 0), (4, 1), (4, 2), (4, 3), (4, 4)]
What am I forgetting?
Dec 19 '06 #14
Not sure what you're looking up, but I get the listcomp. That does what I was thinking; it didn't occur to me to use the inline string formatting to achieve the desired result.
I'm still having my original dilemma on these results.

I see how the listcomp generates the strings, but how do I make each string result become the name of a variable that I use to instantiate my object, all "hands off"?

(I'm working on this part right now...)
Dec 19 '06 #15
I'm still having my original dilemma on these results.

I see how the listcomp generates the strings, but how do I make each string result become the name of a variable that I use to instantiate my object, all "hands off"?

(I'm working on this part right now...)
Skip that -- I found it:

Expand|Select|Wrap|Line Numbers
  1. self.name=name # passed in when instantiated
  2.  
Dec 19 '06 #16
bartonc
6,596 Expert 4TB
Skip that -- I found it:

Expand|Select|Wrap|Line Numbers
  1. self.name=name # passed in when instantiated
  2.  
actually I was thinking of this line:
Expand|Select|Wrap|Line Numbers
  1.     # it is legal to access the variables directly
  2.     print inst1.cv1
It is not only legal, but common practice. I, however, try to avoid it.
Dec 19 '06 #17
It is not only legal, but common practice. I, however, try to avoid it.
I know you prefer setters and getters. (You'll be proud of me in my results post, in a minute). I get that it could be dicey if you write to vars from the outside, based on our earlier discussion. I'm not as clear on why it's a bad practice to "read", or return results from, internal vars, however. Is it just a purity thing?
Dec 19 '06 #18
Early results:

Here's BingoCard() that consists of 25 Square() objects. The instances are named individually (thanks Barton!) and also stored in a compound list, and able to be referenced directly (item[x][y]).

I chose to skip the listcomp for now to simplify my own comprehension (ptp). However, I've seen evidence that the listcomp is actually more efficient, and I will make the conversion after all of my logic is worked out.

Here's my starter code:

Expand|Select|Wrap|Line Numbers
  1. import random
  2.  
  3. class BingoCard:
  4.  
  5.     def __init__(self):
  6.         self.card=[]
  7.     def FillGrid(self):
  8.         for row in range(5):
  9.             self.rowlist=[]
  10.             for col in range(5):
  11.                 self.rowlist.append(Square(self,"%d"%row,"%d"%col))
  12.             self.card.append(self.rowlist[:])
  13.  
  14.     def ShowGrid(self):
  15.         for row in self.card:
  16.             print 
  17.             for sq in row:
  18.                 sq.ShowName()
  19.  
  20. class Square:
  21.  
  22.     def __init__(self, parent, row,col):
  23.         self.row=row
  24.         self.col=col
  25.         self.name="square-"+str(row)+"-"+str(col)
  26.         self.parent=parent
  27.  
  28.     def ShowName(self):
  29.         print self.name+"  ",
  30.  
  31.  
  32. card=BingoCard()
  33. card.FillGrid()
  34. card.ShowGrid()
  35. print
  36. print
  37. card.card[2][1].ShowName()
and here's my output:

Expand|Select|Wrap|Line Numbers
  1. square-0-0   square-0-1   square-0-2   square-0-3   square-0-4  
  2. square-1-0   square-1-1   square-1-2   square-1-3   square-1-4  
  3. square-2-0   square-2-1   square-2-2   square-2-3   square-2-4  
  4. square-3-0   square-3-1   square-3-2   square-3-3   square-3-4  
  5. square-4-0   square-4-1   square-4-2   square-4-3   square-4-4  
  6.  
  7. square-2-1  
Now that the object distribution is working, I can replicate it for the BingoMixer() and BingoBall()s, and start adding smarts to the objects. I'm pretty psyched. :)
Dec 19 '06 #19
bartonc
6,596 Expert 4TB
Getting there... one little thing that I noticed:
There's no need for the extra lookup required by self. references here.
Expand|Select|Wrap|Line Numbers
  1.     def FillGrid(self):
  2.         for row in range(5):
  3.             self.rowlist=[]
  4.             for col in range(5):
  5.                 self.rowlist.append(Square(self,"%d"%row,"%d"%col))
  6.             self.card.append(self.rowlist[:])
  7.  
Use function scope variables for things like this. ie
Expand|Select|Wrap|Line Numbers
  1.     def FillGrid(self):
  2.         for row in range(5):
  3.             rowlist=[]
  4.             for col in range(5):
  5.                 rowlist.append(Square(self,"%d"%row,"%d"%col))
  6.             self.card.append(rowlist[:])    # since you want [:] probably don't need to slice
  7.  
Dec 19 '06 #20
Getting there... one little thing that I noticed:
There's no need for the extra lookup required by self. references here.
OK, I got the function (method?) local thing.

I was concerned that, because I was using rowlist in a loop, to populate the card, that I didn't want any crosslinks between items on different rows, so I forced a copy [:] in the append. I see that wasn't necessary after trying it, but I err on the side of caution since all those rules for mutables aren't yet second-nature for me.
Dec 19 '06 #21
BINGO!!!

I've got a working model.

Don't know if I should post the code, it's a little over 200 lines. Could probably be more elegant, but the logic's good, and objects abound!

Thanks guys. Give me the go-ahead if I should put it up here; if it's too much, no problem. Is there a separate area for uploads etc.?
Dec 19 '06 #22
bartonc
6,596 Expert 4TB
BINGO!!!

I've got a working model.

Don't know if I should post the code, it's a little over 200 lines. Could probably be more elegant, but the logic's good, and objects abound!

Thanks guys. Give me the go-ahead if I should put it up here; if it's too much, no problem. Is there a separate area for uploads etc.?
Post away (up to 10000 character limit).
Dec 19 '06 #23
bartonc
6,596 Expert 4TB
OK, I got the function (method?) local thing.
Yes, to use proper OO parlance, "method" is, of course, correct. Scope rules for functions still apply, though.
I was concerned that, because I was using rowlist in a loop, to populate the card, that I didn't want any crosslinks between items on different rows, so I forced a copy [:] in the append. I see that wasn't necessary after trying it, but I err on the side of caution since all those rules for mutables aren't yet second-nature for me.
What is actually taking place is:
1. create an empty list and assign it to the name "rowlist"
2. fill it in the loop
-now here's the fun part-
3. assign a reference (self.card[i]) to that object (internally, refcount += 1) - creating two refferences to the same object
4. reassign the value of "rowlist" to an empty list (refcount -= 1) - only self.card[i] remains "pointing" to to the list.
and so on.

As a bonus when refcount = 0 the object will be garbage collected. This is the reason we don't ever worry about memory management. Some programmers will
Expand|Select|Wrap|Line Numbers
  1. del objcect
to free up memory immediately and make the reference to "object" invalid, some will
Expand|Select|Wrap|Line Numbers
  1. object = None
(which keeps "object" assigned, but releases the memory for GC). For large objects the former is probably a good practice. Most often it's OK to just leave the object hanging around.
Dec 19 '06 #24
Post away (up to 10000 character limit).
Okie Dokie.

Let's start with my output. We start with a randomly generated bingo card. Then we're given the number of balls pulled from the machine, and their order, followed by BINGO!!!, and the final winning card with matched numbers blacked out for the visual effect. This allows us to validate the program operation against the initial random values chosen.

Expand|Select|Wrap|Line Numbers
  1. B    I    N    G    O
  2.  
  3. 8    26    36    48    75    
  4. 2    22    38    58    64    
  5. 10    24    ##    51    63    
  6. 3    30    44    59    69    
  7. 15    25    41    53    62    
  8.  
  9. 30 balls drawn:
  10.  
  11. 46 55 53 26 58 16 30 22 64 49 67 32 50 4 21 20 6 2 18 27 62 11 23 15 35 5 51 66 47 38
  12.  
  13. BINGO!!!
  14.  
  15. B    I    N    G    O
  16.  
  17. 8    ##    36    48    75    
  18. ##    ##    ##    ##    ##    
  19. 10    24    ##    ##    63    
  20. 3    ##    44    59    69    
  21. ##    25    41    ##    ##    
Of course, while testing, I had stuff printing all over the place at various intervals, but in the end it condensed nicely to this compact display.

The code has a lot of logic redundancy since the BingoCard and BingoSquare behave very similarly to the BingoMixer and BingoBall, with the exception of the 5x5 requirement for the layout of the card, and the fact that each column only contains a subset of the random numbers drawn, ensuring a somewhat smooth distribution across the card.

My only lazy point, I realize this morning, is that I believe the numbers in a column on a real bingo card are sorted in ascending order. If I'd thought of this, I probably would have populated them in a column-first order, which would allow me to sort the elements of that sublist easily. As it is, I generated the card in a row-first order, and although I can visualize the solution, I don't feel like messing with it right now. :) So here goes:

Expand|Select|Wrap|Line Numbers
  1. import random
  2.  
  3. class BingoCard:
  4.  
  5.     def __init__(self,prnt):
  6.         self.card=[]
  7.         self.parent=prnt
  8.         self.BingoPatterns=[\
  9.                 [[0,0],[0,4],[2,2],[4,0],[4,4]],\
  10.                 [[0,0],[1,1],[2,2],[3,3],[4,4]],\
  11.                 [[4,0],[3,1],[2,2],[1,3],[0,4]],\
  12.                 [[0,0],[0,1],[0,2],[0,3],[0,4]],\
  13.                 [[1,0],[1,1],[1,2],[1,3],[1,4]],\
  14.                 [[2,0],[2,1],[2,2],[2,3],[2,4]],\
  15.                 [[3,0],[3,1],[3,2],[3,3],[3,4]],\
  16.                 [[4,0],[4,1],[4,2],[4,3],[4,4]],\
  17.                 [[0,0],[1,0],[2,0],[3,0],[4,0]],\
  18.                 [[0,1],[1,1],[2,1],[3,1],[4,1]],\
  19.                 [[0,2],[1,2],[2,2],[3,2],[4,2]],\
  20.                 [[0,3],[1,3],[2,3],[3,3],[4,3]],\
  21.                 [[0,4],[1,4],[2,4],[3,4],[4,4]]\
  22.                 ]
  23.  
  24.     def FillCard(self):
  25.         if len(self.card):
  26.             for row in self.card:
  27.                 for square in row:
  28.                     square.ResetMatched()
  29.                     square.ResetNumber()                
  30.         else:
  31.             for row in range(5):
  32.                 rowlist=[]
  33.                 for col in range(5):
  34.                     rowlist.append(BingoSquare(self,"%d"%row,"%d"%col))
  35.                 self.card.append(rowlist)
  36.  
  37.     def ShowCard(self):
  38.         print; print'B\tI\tN\tG\tO'
  39.         for row in self.card:
  40.             print
  41.             for square in row:
  42.                 if square.GetMatched():
  43.                     print '##\t',
  44.                 else:                    
  45.                     print '%d\t' % square.GetNumber(),
  46.         print
  47.  
  48.     def NewGame(self):
  49.         NumbersLeft=[]
  50.         for i in range(5):
  51.             NumbersLeft.append(range(1+(i*15),16+(i*15)))
  52.         self.FillCard()
  53.         for row in self.card:
  54.             for i in range(5):                
  55.                 rnum=random.randint(0,len(NumbersLeft[i])-1)
  56.                 row[i].SetNumber(NumbersLeft[i][rnum])
  57.                 #print i,NumbersLeft[i][rnum]
  58.                 #print square.GetNumber()
  59.                 NumbersLeft[i]=NumbersLeft[i][:rnum]+NumbersLeft[i][rnum+1:]
  60.                 #print NumbersLeft        
  61.         self.card[2][2].SetNumber(0)
  62.         self.card[2][2].SetMatched()  
  63.         #self.ShowCard()
  64.  
  65.     def CheckMatches(self,num):
  66.         for row in self.card:
  67.             for square in row:
  68.                 if square.GetNumber()==num:
  69.                     square.SetMatched()
  70.  
  71.     def CheckBingo(self):
  72.         for set in self.BingoPatterns:
  73.             bingo=True            
  74.             for coords in set:
  75.                 if not self.card[coords[0]][coords[1]].GetMatched():
  76.                     bingo=False
  77.                     break                    
  78.             if bingo: return True
  79.         return False
  80.  
  81.  
  82. class BingoSquare:
  83.  
  84.     def __init__(self, prnt, row,col):
  85.         self.row=row
  86.         self.col=col
  87.         self.name="sqr"+str(row)+str(col)
  88.         self.parent=prnt
  89.         self.number=0
  90.         self.matched=False
  91.  
  92.     def ShowName(self):
  93.         pass
  94.         print self.name+"  ",
  95.  
  96.     def SetNumber(self,num):
  97.         self.number=num
  98.  
  99.     def ResetNumber(self):
  100.         self.number=None
  101.  
  102.     def GetNumber(self):
  103.         return self.number
  104.  
  105.     def SetMatched(self):
  106.         self.matched=True
  107.  
  108.     def ResetMatched(self):
  109.         self.matched=False
  110.  
  111.     def GetMatched(self):
  112.         return self.matched
  113.  
  114.  
  115.  
  116. class BingoMixer:
  117.  
  118.     def __init__(self,prnt):
  119.         self.balls=[]
  120.         self.parent=prnt
  121.  
  122.     def FillBox(self):
  123.         if len(self.balls):
  124.             for ball in self.balls:
  125.                 ball.ResetDrawn()
  126.                 ball.ResetNumber()                
  127.         else:
  128.             for ball in range(75):
  129.                 self.balls.append(BingoBall(self,"%d"%ball))
  130.  
  131.     def ShowBox(self):
  132.         for ball in self.balls:
  133.             ball.ShowName()
  134.  
  135.     def ShowBalls(self):
  136.         for ball in self.balls:
  137.             print ball.GetNumber(), # ball.GetDrawn()
  138.  
  139.     def ShowDrawn(self):
  140.         for i in range(len(self.balls)):
  141.             if not self.balls[i].GetDrawn():
  142.                 self.drawnBalls=self.balls[:i]
  143.                 print; print '%d balls drawn:' % len(self.drawnBalls)
  144.                 print
  145.                 for ball in self.drawnBalls:                    
  146.                     print ball.GetNumber(),
  147.                 break
  148.         print
  149.  
  150.     def NewGame(self):
  151.         NumbersLeft=range(1,76)
  152.         self.FillBox()
  153.         for ball in self.balls:
  154.             rnum=random.randint(0,len(NumbersLeft)-1)
  155.             ball.SetNumber(NumbersLeft[rnum])
  156.             #print ball.GetNumber()
  157.             NumbersLeft=NumbersLeft[:rnum]+NumbersLeft[rnum+1:]
  158.             #print NumbersLeft
  159.         #self.ShowBalls()      
  160.  
  161.  
  162. class BingoBall:
  163.  
  164.     def __init__(self, parent, ballno):
  165.         self.name="ball"+str(ballno)
  166.         self.parent=parent
  167.         self.number=0
  168.         self.drawn=False
  169.  
  170.     def ShowName(self):
  171.         print self.name,
  172.  
  173.     def SetNumber(self,num):
  174.         self.number=num
  175.  
  176.     def ResetNumber(self):
  177.         self.number=None
  178.  
  179.     def GetNumber(self):
  180.         return self.number
  181.  
  182.     def SetDrawn(self):
  183.         self.drawn=True
  184.  
  185.     def ResetDrawn(self):
  186.         self.drawn=False
  187.  
  188.     def GetDrawn(self):
  189.         return self.drawn
  190.  
  191. class BingoGame:
  192.     import random
  193.     random.seed()
  194.  
  195.     def __init__(self):        
  196.         self.card=BingoCard(self)
  197.         self.mixer=BingoMixer(self)
  198.  
  199.     def Play(self):
  200.         bingo=False
  201.         self.card.NewGame()
  202.         self.mixer.NewGame()
  203.         self.card.ShowCard()
  204.  
  205.         for ball in self.mixer.balls:
  206.             thisNum=ball.GetNumber()
  207.             ball.SetDrawn()
  208.             #print;print thisNum
  209.             self.card.CheckMatches(thisNum)
  210.             #self.card.ShowCard()
  211.             if self.card.CheckBingo():
  212.                 break
  213.  
  214.         self.mixer.ShowDrawn()    
  215.         print; print 'BINGO!!!'
  216.         self.card.ShowCard()
  217.         quit()
  218.  
  219.  
  220. if __name__ == '__main__':
  221.     game=BingoGame()
  222.     game.Play()
  223.  
I left my quit() in an inopportune place, but it doesn't really affect the outcome. What's the proper way to end something? Just have it come back to main and drop off the bottom, or do you have to load and use sys.exit() all the time?

Plenty of room here for tightening, especially since my use of objects was an academic exercise. Learning about object use and efficiency was my primary goal here, so object-related critique would be most helpful to my learning process.

Thanks all!
Dec 19 '06 #25
bvdet
2,851 Expert Mod 2GB
Mark,

That's really cool. It works fine in Pythonwin without the quit(). Thanks for posting.

BV
Dec 19 '06 #26
bartonc
6,596 Expert 4TB
The code has a lot of logic redundancy since the BingoCard and BingoSquare behave very similarly to the BingoMixer and BingoBall, with the exception of the 5x5 requirement for the layout of the card, and the fact that each column only contains a subset of the random numbers drawn, ensuring a somewhat smooth distribution across the card.
I'm still having a look at this. I may be time to start exploring "inheritance" where a super class has the common elements in it and subclasses inherit and/or override aspects of the parent class.

What's the proper way to end something? Just have it come back to main and drop off the bottom?
That's it!
Plenty of room here for tightening, especially since my use of objects was an academic exercise. Learning about object use and efficiency was my primary goal here, so object-related critique would be most helpful to my learning process.
As I say, I'm still having a look. I'll get back to you.
PS It's good to see you messing around in the other forums. It can be fun.
Dec 20 '06 #27
bartonc
6,596 Expert 4TB
What is actually taking place is:
1. create an empty list and assign it to the name "rowlist"
2. fill it in the loop
-now here's the fun part-
3. assign a reference (self.card[i]) to that object (internally, refcount += 1) - creating two refferences to the same object
4. reassign the value of "rowlist" to an empty list (refcount -= 1) - only self.card[i] remains "pointing" to to the list.
and so on.
The same type of thing happens when a function uses a local (functions scope) variable to (say) build a list and then returns that list to the caller. The local reference parishes but the object is kept alive because the caller now has a reference to it.
Dec 21 '06 #28

Post your reply

Sign in to post your reply or Sign up for a free account.

By using this site, you agree to our Privacy Policy and Terms of Use.