Browse Source

Add Deck of Cards solution

Donne Martin 8 years ago
parent
commit
0f517c709e

+ 0 - 0
solutions/object_oriented_design/deck_of_cards/__init__.py


+ 195 - 0
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
+}

+ 116 - 0
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):  # ...