Keith Winston wrote: > I've put together my first small program. It's a simulation of the game > Chutes & Ladders. It plays the game and amasses an array of ([multicount] > [gamecount]) size, and then crunches simple stats on the average moves, > chutes, and ladders for all games in each high-level (multi) pass. > Hopefully the code is clear. > > I don't think I really thought out the OOP element properly... I was > thinking of accomodating a multiplayer version in the future, but that > would require a significant rewrite. Perhaps Games should be a separate > class, I still don't really have OOP down (I'm learning Python, OOP, and > Linux simultaneously). There's no interaction between players in the game, > so there's not really any reason to do a multiplayer version: I was just > using this to familiarize myself with basic Python and maybe some stats.
You could add a rule that a second player arriving on a field can kick out the current occupant -- like in http://en.wikipedia.org/wiki/Mensch_ärgere_dich_nicht > I'd be interested in ALL feedback: aesthetic, functional, design, > whatever. I would have used numpy but I couldn't get it installed... I > noticed, > belatedly, that the math module has arrays, I didn't look to see if they > would have made sense to use, I think I remember hearing something about > them being inefficient or something. Anyway, thanks. My random remarks: > #Chutes & Ladders Simulation 1.0 > import random > > # Landing on a chute (key) causes one to slide down to the corresponding > value. > chutes = {16: 6, 47: 26, 49: 11, 56: 53, 62: 19, 64: 60, 87: 24, 93: 73, > 95: 75, 98:78} > > # Landing on a ladder (key) causes one to slide up to the corresponding > value. > ladders = {1: 38, 4: 14, 9: 31, 21: 42, 28: 84, 36: 44, 51: 67, 71: 91, > 80:100} > > class Player: > """Player class for Chutes & Ladders.""" > > def __init__(self): Consider providing chutes and ladders as arguments to avoid the implicit dependency from global variables. > self.reset() > > def reset(self): > self.position = 0 > self.movecount = 0 > self.numchutes = 0 > self.numladders = 0 > > def move(self): > """Single move, with chutes, ladders, & out of bounds""" > roll = random.randint(1,6) > self.movecount += 1 > self.position += roll > if self.position in chutes.keys(): > self.position = chutes.get(self.position) > self.numchutes += 1 .keys() is redundant in Python 3 (and does a lot of extra work in Python 2). When you already know that key is in somedict `somedict[key]` is more efficient than `somedict.get(key)` and has the same result. I would write the above two lines as if self.position in chutes: self.position = chutes[self.position] > elif self.position in ladders.keys(): > self.position = ladders.get(self.position) > self.numladders += 1 When you look at the code you see that chutes and ladders are handled the same way. You might consider using a single dictionary for both. > elif self.position > 100: # move doesn't count, have to land > exactly > self.position -= roll > > def game(self): > """Single game""" > self.reset() > while self.position < 100: > self.move() > > def gameset(self, reps): > """A set of games, returning associated stats array""" > setstats = [] > for i in range(reps): > self.game() > stat = [i, self.movecount, self.numchutes, self.numladders] As you are not planning to append items to `stat` a tuple is more idiomatic here than a list. For extra readability try collections.namedtuple which allows accessing the data in the tuple as (for example) `stat.numchutes` instead of the hard to remember stat[3]. > setstats.append(stat) > return setstats > > def multisets(self, multi, reps): > """A set of game sets, adding another dim to the stats array""" > multistats = [] > for i in range(multi): > set1 = p1.gameset(reps) That should be set1 = self.gameset(reps) > multistats.append(set1) > return multistats I'd probably remove the multisets() method from the class to keep the interface small and replicate the functionality in client code with games = [p1.gameset(gamecount) for _ in range(multicount)] I might do the same for gameset() (and have game() return the statistics for a single game). > p1 = Player() > gamecount = 1000 > multicount = 10 > games = p1.multisets(multicount, gamecount) > print("Avg moves Avg chutes Avg ladders") > for i in range(multicount): > tmulti = games[i] In Python you can iterate over a list directly. With that in mind... > summoves, sumchutes, sumladders = 0, 0, 0 > for j in range(gamecount): > tgset = tmulti[j] > summoves += tgset[1] > sumchutes += tgset[2] > sumladders += tgset[3] your two loops can be simplified: for tmulti in games: ... for tgset in tmulti: ... ... > print(str(summoves/gamecount).rjust(9), \ > str(sumchutes/gamecount).rjust(12), \ > str(sumladders/gamecount).rjust(13)) Once you become familiar with format strings you may find print("{moves:9.2f} {chutes:12.2f} {ladders:13.2f}".format( moves=summoves/gamecount, chutes=sumchutes/gamecount, ladders=sumladders/gamecount)) easier to maintain. You could also factor out the calculation of the average into a separate function. Your code would then become (untested) def mean(items): items = list(items) return sum(items) / len(items) print("Avg moves Avg chutes Avg ladders") for tmulti in games: print("{moves:9.2f} {chutes:12.2f} {ladders:13.2f}".format( moves=mean(tgset[1] for tgset in tmulti), chutes=mean(tgset[2] for tgset in tmulti), ladders=mean(tgset[3] for tgset in tmulti) )) with the option to replace my naive mean() with a better implementation. > Thanks for any suggestions or thoughts. I know this is a very simple > program, but I'm very pleased that, once I began to sort out the basics, > it fell together pretty readily: I really like Python, though there's a > lot to learn. FYI, I recently played C & L with a 4 y.o. friend, it is not > otherwise my game of choice ;-) To sum it up: I like what you have, my hints are all about very minor points :) _______________________________________________ Tutor maillist - [email protected] To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
