From 0f517c709efcbad1643694189874f4f78fc32f00 Mon Sep 17 00:00:00 2001 From: Donne Martin Date: Fri, 3 Mar 2017 18:51:00 -0800 Subject: [PATCH] Add Deck of Cards solution --- .../deck_of_cards/__init__.py | 0 .../deck_of_cards/deck_of_cards.ipynb | 195 ++++++++++++++++++ .../deck_of_cards/deck_of_cards.py | 116 +++++++++++ 3 files changed, 311 insertions(+) create mode 100644 solutions/object_oriented_design/deck_of_cards/__init__.py create mode 100644 solutions/object_oriented_design/deck_of_cards/deck_of_cards.ipynb create mode 100644 solutions/object_oriented_design/deck_of_cards/deck_of_cards.py diff --git a/solutions/object_oriented_design/deck_of_cards/__init__.py b/solutions/object_oriented_design/deck_of_cards/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/solutions/object_oriented_design/deck_of_cards/deck_of_cards.ipynb b/solutions/object_oriented_design/deck_of_cards/deck_of_cards.ipynb new file mode 100644 index 00000000..3c19e1fb --- /dev/null +++ b/solutions/object_oriented_design/deck_of_cards/deck_of_cards.ipynb @@ -0,0 +1,195 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/donnemartin/system-design-primer-primer)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Design a deck of cards" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Constraints and assumptions\n", + "\n", + "* Is this a generic deck of cards for games like poker and black jack?\n", + " * Yes, design a generic deck then extend it to black jack\n", + "* Can we assume the deck has 52 cards (2-10, Jack, Queen, King, Ace) and 4 suits?\n", + " * Yes\n", + "* Can we assume inputs are valid or do we have to validate them?\n", + " * Assume they're valid" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Solution" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting deck_of_cards.py\n" + ] + } + ], + "source": [ + "%%writefile deck_of_cards.py\n", + "from abc import ABCMeta, abstractmethod\n", + "from enum import Enum\n", + "import sys\n", + "\n", + "\n", + "class Suit(Enum):\n", + "\n", + " HEART = 0\n", + " DIAMOND = 1\n", + " CLUBS = 2\n", + " SPADE = 3\n", + "\n", + "\n", + "class Card(metaclass=ABCMeta):\n", + "\n", + " def __init__(self, value, suit):\n", + " self.value = value\n", + " self.suit = suit\n", + " self.is_available = True\n", + "\n", + " @property\n", + " @abstractmethod\n", + " def value(self):\n", + " pass\n", + "\n", + " @value.setter\n", + " @abstractmethod\n", + " def value(self, other):\n", + " pass\n", + "\n", + "\n", + "class BlackJackCard(Card):\n", + "\n", + " def __init__(self, value, suit):\n", + " super(BlackJackCard, self).__init__(value, suit)\n", + "\n", + " def is_ace(self):\n", + " return True if self._value == 1 else False\n", + "\n", + " def is_face_card(self):\n", + " \"\"\"Jack = 11, Queen = 12, King = 13\"\"\"\n", + " return True if 10 < self._value <= 13 else False\n", + "\n", + " @property\n", + " def value(self):\n", + " if self.is_ace() == 1:\n", + " return 1\n", + " elif self.is_face_card():\n", + " return 10\n", + " else:\n", + " return self._value\n", + "\n", + " @value.setter\n", + " def value(self, new_value):\n", + " if 1 <= new_value <= 13:\n", + " self._value = new_value\n", + " else:\n", + " raise ValueError('Invalid card value: {}'.format(new_value))\n", + "\n", + "\n", + "class Hand(object):\n", + "\n", + " def __init__(self, cards):\n", + " self.cards = cards\n", + "\n", + " def add_card(self, card):\n", + " self.cards.append(card)\n", + "\n", + " def score(self):\n", + " total_value = 0\n", + " for card in card:\n", + " total_value += card.value\n", + " return total_value\n", + "\n", + "\n", + "class BlackJackHand(Hand):\n", + "\n", + " BLACKJACK = 21\n", + "\n", + " def __init__(self, cards):\n", + " super(BlackJackHand, self).__init__(cards)\n", + "\n", + " def score(self):\n", + " min_over = sys.MAXSIZE\n", + " max_under = -sys.MAXSIZE\n", + " for score in self.possible_scores():\n", + " if self.BLACKJACK < score < min_over:\n", + " min_over = score\n", + " elif max_under < score <= self.BLACKJACK:\n", + " max_under = score\n", + " return max_under if max_under != -sys.MAXSIZE else min_over\n", + "\n", + " def possible_scores(self):\n", + " \"\"\"Return a list of possible scores, taking Aces into account.\"\"\"\n", + " # ...\n", + "\n", + "\n", + "class Deck(object):\n", + "\n", + " def __init__(self, cards):\n", + " self.cards = cards\n", + " self.deal_index = 0\n", + "\n", + " def remaining_cards(self):\n", + " return len(self.cards) - deal_index\n", + "\n", + " def deal_card():\n", + " try:\n", + " card = self.cards[self.deal_index]\n", + " card.is_available = False\n", + " self.deal_index += 1\n", + " except IndexError:\n", + " return None\n", + " return card\n", + "\n", + " def shuffle(self): # ..." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.4.3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/solutions/object_oriented_design/deck_of_cards/deck_of_cards.py b/solutions/object_oriented_design/deck_of_cards/deck_of_cards.py new file mode 100644 index 00000000..2ec4f1bc --- /dev/null +++ b/solutions/object_oriented_design/deck_of_cards/deck_of_cards.py @@ -0,0 +1,116 @@ +from abc import ABCMeta, abstractmethod +from enum import Enum +import sys + + +class Suit(Enum): + + HEART = 0 + DIAMOND = 1 + CLUBS = 2 + SPADE = 3 + + +class Card(metaclass=ABCMeta): + + def __init__(self, value, suit): + self.value = value + self.suit = suit + self.is_available = True + + @property + @abstractmethod + def value(self): + pass + + @value.setter + @abstractmethod + def value(self, other): + pass + + +class BlackJackCard(Card): + + def __init__(self, value, suit): + super(BlackJackCard, self).__init__(value, suit) + + def is_ace(self): + return True if self._value == 1 else False + + def is_face_card(self): + """Jack = 11, Queen = 12, King = 13""" + return True if 10 < self._value <= 13 else False + + @property + def value(self): + if self.is_ace() == 1: + return 1 + elif self.is_face_card(): + return 10 + else: + return self._value + + @value.setter + def value(self, new_value): + if 1 <= new_value <= 13: + self._value = new_value + else: + raise ValueError('Invalid card value: {}'.format(new_value)) + + +class Hand(object): + + def __init__(self, cards): + self.cards = cards + + def add_card(self, card): + self.cards.append(card) + + def score(self): + total_value = 0 + for card in card: + total_value += card.value + return total_value + + +class BlackJackHand(Hand): + + BLACKJACK = 21 + + def __init__(self, cards): + super(BlackJackHand, self).__init__(cards) + + def score(self): + min_over = sys.MAXSIZE + max_under = -sys.MAXSIZE + for score in self.possible_scores(): + if self.BLACKJACK < score < min_over: + min_over = score + elif max_under < score <= self.BLACKJACK: + max_under = score + return max_under if max_under != -sys.MAXSIZE else min_over + + def possible_scores(self): + """Return a list of possible scores, taking Aces into account.""" + # ... + + +class Deck(object): + + def __init__(self, cards): + self.cards = cards + self.deal_index = 0 + + def remaining_cards(self): + return len(self.cards) - deal_index + + def deal_card(): + try: + card = self.cards[self.deal_index] + card.is_available = False + self.deal_index += 1 + except IndexError: + return None + return card + + def shuffle(self): # ... \ No newline at end of file