Back in 2006, blogger Marlinschamps proposed the rules for the game of victimhood poker. In a spare couple of hours last weekend, I decided to code this up so that we had an implementation of it. Beloved readers, here is that implementation. It's in Python; I show it in chunks, but it should all go in a single file called e.g. victimhood.py.
First we define the cards in the deck, their points, and their class:
#!/usr/bin/python # This code is in the public domain. Copy and use as you see fit. # Original author: http://hemiposterical.blogspot.com/, credit # would be nice but is not required. import random deck = { # Key: (points,class) 'Black': (14, 'skin'), 'Native-American': (13, 'ethnicity'), 'Muslim': (12, 'religion'), 'Hispanic': (11, 'ethnicity'), 'Transgender': (10, 'gender'), 'Gay': (9, 'none'), 'Female': (8, 'gender'), 'Oriental': (7, 'ethnicity'), 'Handicapped': (6, 'none'), 'Satanist': (6, 'religion'), 'Furry': (5, 'none'), 'Non-Christian': (4, 'religion'), 'East-Indian': (3, 'ethnicity'), 'Hindu': (3, 'religion'), 'Destitute': (2, 'economic'), 'White': (0, 'skin'), 'Straight': (0, 'gender'), 'Christian': (0, 'religion'), 'Bourgeois': (0, 'economic'), } # Categories in the order you'd describe someone category_list = [ 'economic','none','skin','religion','ethnicity','gender', ] categories = set(category_list)In addition, a couple of helper functions to make it easier to ask questions about a specific card:
def cardscore(card): """ How much does this card score? """ (s, unused_cls) = deck[card] return s def cardclass(card): """ What class does this card represent? """ (unused_s, cls) = deck[card] return clsNow we define what a "hand" is, with a bunch of functions to make it easier to merge other cards into a hand and compute the best score and hand from these cards:
class Hand(object): """ A hand is a list of cards with some associated scoring functions """ def __init__(self, start_cards=None): if start_cards is None: self.cards = [] else: self.cards = start_cards[:] def add(self, card): self.cards.append(card) def bestscore(self): (score, bestcards) = self.besthand() return score def bestcards(self): (score, bestcards) = self.besthand() return bestcards def besthand(self): """ What's the highest possible score for this hand? Limitations: one card per class, no more than 5 cards in total Return (score, best_hand) """ score_by_class = { } card_by_class = { } for card in self.cards: try: s = cardscore(card) card_class = cardclass(card) except KeyError, err: raise KeyError("Invalid card name '%s'" % card) if card_class not in score_by_class: score_by_class[card_class] = s if s >= score_by_class[card_class]: score_by_class[card_class] = s card_by_class[card_class] = card # We now have the best scoring card in each # class. But we can only use the best 5. cards = card_by_class.values() cards.sort(lambda x,y: cmp(cardscore(x),cardscore(y))) if len(cards) > 5: cards = cards[0:5] tot = 0 for card in cards: tot += cardscore(card) best_hand = Hand(cards) return (tot, best_hand) def merge(self, hand): """ Merge this hand and another to return a new one """ ans = self.copy() for c in hand.cards: ans.add(c) return ans def copy(self): return Hand(self.cards) def __str__(self): return ', '.join(['%s (%d)' % (c, cardscore(c)) for c in self.cards]) def card_in_class(self,class_name): """Returns a card in the given class, if the hand has one""" for card in self.cards: (s,c) = deck[card] if c == class_name: return card # No match return None def description(self): card_order = [self.card_in_class(c) for c in category_list] card_order = filter(lambda x: x is not None, card_order) return ' '.join(card_order)Now we can define a game with a number of players, and specify how many copies of the deck we want to use for the game:
class Game(object): def __init__(self, player_count, deck_multiple=2): self.player_count = player_count self.deck_multiple = deck_multiple self.player_hands = { } for i in range(1,1+player_count): self.player_hands[i] = Hand() self.shuffle_deck() self.community = Hand() def shuffle_deck(self): self.deck = [] for i in range(self.deck_multiple): self.deck.extend(deck.keys()) random.shuffle(self.deck) def deal(self, cards_per_player): for p in range(1,1+self.player_count): for c in range(cards_per_player): card = self.deck.pop() # might run out self.player_hands[p].add(card) def deal_community(self, community_cards): self.community = Hand() for c in range(community_cards): card = self.deck.pop() self.community.add(card) def get_community(self): return self.community def best_hand(self, player_num): h = self.player_hands[player_num] # Expand the hand with any community cards h2 = h.merge(self.community) return h2.besthand()Finally, we have some code to demonstrate the game being played. We give 5 cards each to 4 players, and have 3 community cards which they can use. We display each player's best hand and score, and announce the winner:
if __name__ == '__main__': player_count=4 g = Game(player_count=player_count, deck_multiple=2) # Everyone gets 5 cards g.deal(5) # There are 3 community cards g.deal_community(3) print "Community cards: %s\n" % g.get_community() winner = None win_score = 0 for p in range(1,1+player_count): (score, hand) = g.best_hand(p) print "Player %d scores %d with %s" % (p, score, hand) print " which is a %s" % hand.description() if score > win_score: winner = p win_score = score print "\nPlayer %d wins!" % winner
Don't judge my Python, y'all; it's quick and dirty Python 2.7. If I wanted a code review, I'd have set this up in GitHub.
So what does this look like when it runs? Here are a few games played out:
Community cards: Christian (0), Native-American (13), Gay (9)
Player 1 scores 40 with Non-Christian (4), Gay (9), Native-American (13), Black (14)
which is a Gay Black Non-Christian Native-American
Player 2 scores 22 with Christian (0), Bourgeois (0), Gay (9), Native-American (13)
which is a Bourgeois Gay Christian Native-American
Player 3 scores 30 with Destitute (2), Satanist (6), Gay (9), Native-American (13)
which is a Destitute Gay Satanist Native-American
Player 4 scores 42 with Female (8), Gay (9), Muslim (12), Native-American (13)
which is a Gay Muslim Native-American Female
Player 4 wins!
Community cards: Non-Christian (4), Bourgeois (0), Furry (5)
Player 1 scores 24 with Straight (0), Destitute (2), Non-Christian (4), Furry (5), Native-American (13)
which is a Destitute Furry Non-Christian Native-American Straight
Player 2 scores 26 with Bourgeois (0), East-Indian (3), Non-Christian (4), Furry (5), Black (14)
which is a Bourgeois Furry Black Non-Christian East-Indian
Player 3 scores 30 with Bourgeois (0), Non-Christian (4), Furry (5), Oriental (7), Black (14)
which is a Bourgeois Furry Black Non-Christian Oriental
Player 4 scores 33 with Destitute (2), Handicapped (6), Muslim (12), Native-American (13)
 :which is a Destitute Handicapped Muslim Native-American
Player 4 wins!
Community cards: Transgender (10), Muslim (12), Oriental (7)
Player 1 scores 53 with Handicapped (6), Transgender (10), Hispanic (11), Muslim (12), Black (14)
which is a Handicapped Black Muslim Hispanic Transgender
Player 2 scores 33 with Bourgeois (0), White (0), Transgender (10), Hispanic (11), Muslim (12)
which is a Bourgeois White Muslim Hispanic Transgender
Player 3 scores 40 with Furry (5), Transgender (10), Muslim (12), Native-American (13)
which is a Furry Muslim Native-American Transgender
Player 4 scores 37 with Destitute (2), Handicapped (6), Oriental (7), Transgender (10), Muslim (12)
which is a Destitute Handicapped Muslim Oriental Transgender
Player 1 wins!
What does this prove? Nothing really, it was kinda fun to write, but I don't see any earthshaking philosophical insights beyond the fact that it's a rather silly game. But then, that's true for its real life analogue as well.
Programming challenge: build a function to instantiate a Hand() from a string e.g. "black east-indian handicapped female" and use this to calculate the canonical score. Bonus points if you can handle missing hyphens.
No comments:
Post a Comment
All comments are subject to retrospective moderation. I will only reject spam, gratuitous abuse, and wilful stupidity.