patternpythonMinor
War Games whOOP
Viewed 0 times
warwhoopgames
Problem
I am a beginner level programmer and recently I started learning OOP,
so I made a simple war game
```
import random
cardsstart = 4 * range(2, 15)
cardsstart.extend([15, 15]);random.shuffle(cardsstart)
cardslen = len(cardsstart)/2
class cards:
def __init__(self):
"""intializes a set of cards(deck) to the object
"""
global cards
self.deck = [cardsstart.pop() for i in range(cardslen)]
def take(self, crd):
"""appends the card given to the object's list deck"""
crd = [crd] if type(crd) is not list else crd
self.deck.extend(crd)
random.shuffle(self.deck)
def give(self, crd):
"""removes the card given from the object's list deck"""
crd = [crd] if type(crd) is not list else crd
for i in crd:
self.deck.remove(i)
player = cards()
enemy = cards()
while True:
raw_input()
if len(player.deck) == 0:
print 'rival won'
break
elif len(enemy.deck) == 0:
print 'you won'
break
print 'player shown his', player.deck[0]
print 'rival shown his', enemy.deck[0]
if player.deck[0] > enemy.deck[0]:
print 'player took rival\'s card'
player.take(enemy.deck[0])
enemy.give(enemy.deck[0])
elif player.deck[0] == enemy.deck[0]:
print 'WAR'
n = 3
while True:
print 'the war is on. player ruling card is {} and rival ruling card is {}'.format(player.deck[n], enemy.deck[n])
if player.deck[n] > enemy.deck[n]:
print 'player won the war'
player.take(enemy.deck[0:n])
enemy.give(enemy.deck[0:n])
break
elif player.deck[n] == enemy.deck[n]:
print 'cards are equal!another war round starting'
n += 3
else:
print 'rival won the war'
enemy.take(player.deck[0:n])
player.give(player.deck[0:n])
break
so I made a simple war game
```
import random
cardsstart = 4 * range(2, 15)
cardsstart.extend([15, 15]);random.shuffle(cardsstart)
cardslen = len(cardsstart)/2
class cards:
def __init__(self):
"""intializes a set of cards(deck) to the object
"""
global cards
self.deck = [cardsstart.pop() for i in range(cardslen)]
def take(self, crd):
"""appends the card given to the object's list deck"""
crd = [crd] if type(crd) is not list else crd
self.deck.extend(crd)
random.shuffle(self.deck)
def give(self, crd):
"""removes the card given from the object's list deck"""
crd = [crd] if type(crd) is not list else crd
for i in crd:
self.deck.remove(i)
player = cards()
enemy = cards()
while True:
raw_input()
if len(player.deck) == 0:
print 'rival won'
break
elif len(enemy.deck) == 0:
print 'you won'
break
print 'player shown his', player.deck[0]
print 'rival shown his', enemy.deck[0]
if player.deck[0] > enemy.deck[0]:
print 'player took rival\'s card'
player.take(enemy.deck[0])
enemy.give(enemy.deck[0])
elif player.deck[0] == enemy.deck[0]:
print 'WAR'
n = 3
while True:
print 'the war is on. player ruling card is {} and rival ruling card is {}'.format(player.deck[n], enemy.deck[n])
if player.deck[n] > enemy.deck[n]:
print 'player won the war'
player.take(enemy.deck[0:n])
enemy.give(enemy.deck[0:n])
break
elif player.deck[n] == enemy.deck[n]:
print 'cards are equal!another war round starting'
n += 3
else:
print 'rival won the war'
enemy.take(player.deck[0:n])
player.give(player.deck[0:n])
break
Solution
!!NO GLOBAL VARIABLES!!
That merits being capitalized. And underlined. And lots of exclamation points. Nothing can kill your ability to write good, modular quite quite like global variables. You have several of them, and it took me several reads to understand what exactly is going on here.
Let's simplify. What do we need for WAR? We need a Deck:
We can start with the default deck, this will be all of our cards:
Never write multiple lines of code on one line (your line with the
So anyway, with this deck, we have:
Which we probably want to be able to shuffle:
Now we need to split this in half for our two players. Let's just add a
With which we can do:
Note that we didn't need any global variables here! It's easy to see where this is coming from. We had a full deck of cards, we shuffled it, and split it in half.
NO RELIANCE ON INTERNAL MEMBERS
In Python, everything is public. But that doesn't mean that you should rely on this. You are relying very explicitly on the members and their types. There had better be a
Which operations? Well, we need the pop off the top card:
And we need to push cards onto it:
And we need to check for emptiness:
And that's about it. Now our main loop just has to look like:
Avoiding the globals and the internal memory structure would let you do later improvements to the
That merits being capitalized. And underlined. And lots of exclamation points. Nothing can kill your ability to write good, modular quite quite like global variables. You have several of them, and it took me several reads to understand what exactly is going on here.
Let's simplify. What do we need for WAR? We need a Deck:
class Deck(object):
...We can start with the default deck, this will be all of our cards:
class Deck(object):
def __init__(self, cards=None):
if cards is not None:
self.cards = cards
else:
self.cards = range(13) * 4Never write multiple lines of code on one line (your line with the
;) It'll be hard to even see that the second line exists! I also don't understand your extend([15,15]) line. Why are there 15s? So anyway, with this deck, we have:
full_deck = Deck() # all the cardsWhich we probably want to be able to shuffle:
def shuffle(self):
random.shuffle(self.cards)Now we need to split this in half for our two players. Let's just add a
split() method:def split(self):
half = len(self.cards) // 2
return Deck(self.cards[:half]), Deck(self.cards[half:])With which we can do:
deck.shuffle()
player, enemy = deck.split()Note that we didn't need any global variables here! It's easy to see where this is coming from. We had a full deck of cards, we shuffled it, and split it in half.
NO RELIANCE ON INTERNAL MEMBERS
In Python, everything is public. But that doesn't mean that you should rely on this. You are relying very explicitly on the members and their types. There had better be a
.deck that's a list. Much better to hide the implementation behind an interface that just has a few key operations. Which operations? Well, we need the pop off the top card:
def pop(self):
return self.cards.pop(0)And we need to push cards onto it:
def push(self, vals):
self.cards.extend(vals)And we need to check for emptiness:
def empty(self):
return not self.cardsAnd that's about it. Now our main loop just has to look like:
while True:
if player.empty():
# enemy won
elif enemy.empty():
# player won
player_card = player.pop()
enemy_card = enemy.pop()
if player_card > enemy_card:
player.push([player_card, enemy_card])
elif enemy_card > player_card:
enemy.push([player_card, enemy_card])
else:
repeatAvoiding the globals and the internal memory structure would let you do later improvements to the
Deck internals. Right now we're popping off the top and pushing onto the back, that's not very efficient for list, but it's much better for deque. Maybe we change to that? Then we'd just have to change pop(0) to be popleft(), but none of the callers of our code will have to know that.Code Snippets
class Deck(object):
...class Deck(object):
def __init__(self, cards=None):
if cards is not None:
self.cards = cards
else:
self.cards = range(13) * 4full_deck = Deck() # all the cardsdef shuffle(self):
random.shuffle(self.cards)def split(self):
half = len(self.cards) // 2
return Deck(self.cards[:half]), Deck(self.cards[half:])Context
StackExchange Code Review Q#104816, answer score: 5
Revisions (0)
No revisions yet.