{
 "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)."
   ]
  },
  {
   "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 self._value == 1\n",
    "\n",
    "    def is_face_card(self):\n",
    "        \"\"\"Jack = 11, Queen = 12, King = 13\"\"\"\n",
    "        return 10 < self._value <= 13\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 self.cards:\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
}
