I finished the Python Koans because I wanted to practise Python and I completed the extra assignment which is the Greed Game (see the rules here).
I think there are no flaws but you never know.
import random
class DiceSet(object):
def __init__(self):
self._values = None
@property
def values(self):
return self._values
def roll(self, n):
self._values = []
for i in range(0, n):
val = random.randint(1, 6)
self._values.append(val)
return self._values
class Player(object):
def __init__(self, name=""):
self.name = name
def __str__(self):
if self.name:
return "'{0}'".format(self.name)
else:
return "'Anonymous player'"
def __repr__(self):
return self.__str__()
class Game(object):
def __init__(self, players, dice, goal = 3000):
if len(players) < 2:
raise "This game is for 2 or more players."
self.dice = dice
self.players_scores = dict((player, 0) for player in players)
self.number_of_players = len(self.players_scores)
self.players = [player for player in self.players_scores.keys()]
self.turn = random.randint(0, len(self.players)-1)
self.goal = goal
def play(self):
final_round = False
first_player_to_reach_firnal_round = None
while not final_round:
self._print_turn()
won_score = self._turn_score()
self.players_scores[self.players[self.turn]] += won_score
print "{0} has won {1} points (total {2}).".format(self.players[self.turn], won_score, self.players_scores[self.players[self.turn]])
if self.players_scores[self.players[self.turn]] >= self.goal:
final_round = True
first_player_to_reach_firnal_round = self.turn
print "{0} has reached over {1} points. Next round will be the last round.".format(self.players[self.turn], self.goal)
self.turn = (self.turn + 1) % self.number_of_players
# Last round. The player that reached the goal number of points doesn't play this round.
print "\n\nLAST ROUND!!! Let's see who wins!"
while self.turn != first_player_to_reach_firnal_round:
self._print_turn()
won_score = self._turn_score()
self.players_scores[self.players[self.turn]] += won_score
print "{0} has won {1} points (total {2}).".format(self.players[self.turn], won_score, self.players_scores[self.players[self.turn]])
self.turn = (self.turn + 1) % self.number_of_players
winner = self._winner()
print "\n\nCongratulations, {0}! You have won the Greed Game!".format(winner)
def score(self, dice):
result = 0
if len(dice) <= 5:
dice_dict = dict((i, dice.count(i)) for i in dice)
if dice_dict.get(1) >= 3:
result += 1000
dice_dict[1] -= 3
for number in dice_dict:
if dice_dict.get(number) >= 3:
result += number * 100
dice_dict[number] -= 3
if 1 in dice_dict:
result += dice_dict[1] * 100
dice_dict[1] = 0
if 5 in dice_dict:
result += dice_dict[5] * 50
dice_dict[5] = 0
non_scoring_dice = [die for die in dice_dict.keys() if dice_dict[die] != 0]
return result, non_scoring_dice
def _turn_score(self):
total_score = 0
keep_rolling = False
dice = self.dice.roll(5)
print "\n{0} rolls the dice and gets {1}.".format(self.players[self.turn], dice)
rolling_score, dice = self.score(dice)
if rolling_score > 0:
# If dice is empty it means there were no non-scoring dice after the roll, so the player gets the choice to roll all 5 dice again
number_of_rollable_dice = 5 if not dice else len(dice)
numbers = { 1: "one", 2: "two", 3: "three", 4: "four", 5: "five" }
print "That's a score of {0} points and {1} non-scoring {2} can be rolled again.".format(rolling_score, number_of_rollable_dice, "die" if number_of_rollable_dice == 1 else "dice")
total_score += rolling_score
player_choice = raw_input("\n{0}, will you roll again? (y/n): ".format(self.players[self.turn]))
if player_choice.upper() in ["Y", "YES"]:
keep_rolling = True
while keep_rolling:
dice = self.dice.roll(number_of_rollable_dice)
print "\n{0} rolls the dice and gets {1}.".format(self.players[self.turn], dice)
rolling_score, dice = self.score(dice)
if rolling_score > 0:
number_of_rollable_dice = 5 if not dice else len(dice)
numbers = { 1: "one", 2: "two", 3: "three", 4: "four", 5: "five" }
print "That's a score of {0} points and {1} non-scoring {2} can be rolled again.".format(rolling_score, number_of_rollable_dice, "die" if number_of_rollable_dice == 1 else "dice")
if total_score + rolling_score >= 300:
total_score += rolling_score
print "Since you reached more than 300 points this turn, your turn score is now {0}".format(total_score)
else:
total_score = rolling_score
print "Your turn score is less than 300 points, so now it's just the rolling score, {0} points.".format(total_score)
player_choice = raw_input("\n{0}, will you roll again? (y/n): ".format(self.players[self.turn]))
if player_choice.upper() in ["Y", "YES"]:
keep_rolling = True
else:
keep_rolling = False
else:
print "That's a zero-point roll, you lost your turn and all your won points in this turn."
keep_rolling = False
total_score = 0
else:
print "That's a zero-point roll, you cannot roll again in this turn."
return total_score
def _winner(self):
return max(self.players_scores, key = self.players_scores.get)
def _print_turn(self):
print "\n------------------------------------------\nStarting turn of {0} [Score: {1}]\n".format(self.players[self.turn], self.players_scores[self.players[self.turn]])
def _print_scores(self):
print "\n------------------------------------------\nScores table\n"
for player, score in self.players_scores:
print "{0}: {1} points".format(player, score)
player1 = Player("Player 1")
player2 = Player("Player 2")
players = [ player1, player2 ]
dice = DiceSet()
game = Game(players, dice, 1500)
game.play()
I am looking for comments and answers in these points:
- Is the class hierarchy acceptable?
- Are the methods in the correct classes?
- Should I have arranged the logic differently?
- Comments and tips on my code.
- Suggestions of Python-style ways of doing this
- Improvement and optimizations of my code
- Ideas of how to implement this in more interesting, original or interesting ways