
Show HN: Deck of Cards – A playing card API - crobertsbmw
http://deckofcardsapi.com
======
aarondf
Hey, you did a thing and told people [0]! Good for you. Is it perfect? No
idea, but from reading the comments here a lot of people don't think so. Who
cares though [1]! You're so far ahead of so many people because you actually
did something.

Next time maybe all your GETs will be RESTful, maybe not. Either way, you've
done a thing, put it out there, and are presumably learning a lot by doing so.

Great work.

[0]
[http://carl.flax.ie/dothingstellpeople.html](http://carl.flax.ie/dothingstellpeople.html)
[1] [http://www.garann.com/dev/2013/how-to-blog-about-code-and-
gi...](http://www.garann.com/dev/2013/how-to-blog-about-code-and-give-zero-
fucks/)

~~~
gooseus
I'm seeing a lot more discussion and debate, rather than criticism.

So good job for that too... I think this Deck of Cards API would make a great
case study for analyzing and discussing the varying takes on how a RESTful
interface should handle certain situations which aren't as straightforward as
the typical blog or todo list examples.

~~~
amouat
A lot of the time it doesn't feel like that to me.

People release things they have worked long and hard on, only to get a bunch
of negative comments picking on holes and mistakes.

The weird thing is they are the lucky ones; it's pretty damn hard to make it
onto the front-page of HN now, regardless of the quality of the content.
(Don't believe me? Take a look at how many times some articles have been
reposted and the disparity in number of votes.)

Getting feedback should be great, but not many of us are good at it.
Generally, we only comment on things we care about or find interesting
(ignoring the stuff that is so bad it deserves harsh criticism like storing
passwords in plain-text), but we forget to say that in the comment and only
mention the minor flaw we saw.

I don't have a solution to this, other than to tell posters to be prepared for
criticism and to take each up-vote as a major token of respect in what you've
built.

------
hartror
Nice one! Good on you for building a thing and putting it out there.

A few comments/suggestions:

* You have DEBUG = True in your production Django config. (eg. [http://deckofcardsapi.com/api/](http://deckofcardsapi.com/api/))

* You are mutating state with HTTP GET, this is an anti-pattern for a number of reasons. The common one I use with people I coach is that browsers/proxies will happily cache GET requests unless told not to, but there are a number of other reasons if you read up on REST [1].

* Being a public API in a well known domain this is a good opportunity to make the API self documenting & navigable with a hypermedia format. (eg HAL, Siren, JSON-LD) [2]

[1]
[http://martinfowler.com/articles/richardsonMaturityModel.htm...](http://martinfowler.com/articles/richardsonMaturityModel.html)

[2] [http://sookocheff.com/posts/2014-03-11-on-choosing-a-
hyperme...](http://sookocheff.com/posts/2014-03-11-on-choosing-a-hypermedia-
format/)

~~~
Kiro
> You are mutating state with HTTP GET

I agree. However, this is the first API I've seen where a mutable GET actually
makes sense. Drawing a card is definitely a GET. How would you do it instead?

~~~
xnxn
> Drawing a card is definitely a GET.

I see where you're coming from ("I'm drawing / taking / GETting some cards"),
but drawing cards is not an idempotent action. I would implement it as a POST.

~~~
ryan-allen
I think it's more of a terminology thing, rather than 'GET me a card' you
could 'POST remove 2 cards from the deck' and the side effect is the response
tells you which cards were deleted.

But this kind of bike shedding drives me mad because oh my lord who gives a
crap just build software and document it properly.

~~~
Confusion
I give a crap, because when I see an API, I expect a GET to be idempotent. If
a coworker implemented this, there would be words.

~~~
whyaduck
Same here. Working on a large codebase where people "just build software and
document it properly" without following standards is not fun. Not to mention
that frequently people end up shortening that to "just build software". It's
better to get in the habit of following good standards even on small projects
before bad habits form.

------
alangpierce
While this looks to me like a pretty clean and straightforward API, it's
definitely not a REST API (not that it claims to be):

* The API doesn't really follow HTTP. It allows GET for state-modifying actions, which breaks assumptions that could be made by browsers, client libraries, caches, and proxies. Also, the "success" field seems fishy; a 200 response code is the usual HTTP way to indicate success.

* The API is not resource-oriented; the URLs "shuffle" and "draw" are verbs that describe the action to take. In a "real" REST API, URLs define conceptual resources (nouns), and you interact with the API by interacting with those resources.

* The API does not use hypermedia.

Hypermedia and HTTP compliance are discussed elsewhere in this thread, but I
wonder if others have an opinion on the fact that the API isn't resource-
oriented.

This "resource-oriented" rule has always bugged me about REST APIs. My
impression has been that REST advocates claim that pretty much every API can
and should be implemented with a CRUD-like interface (where the entire API
consists of performing GET, POST, PUT, and DELETE on a conceptual set of
resources), but it seems like that would be really awkward for this "deck of
cards" use case. Is there value in trying to use resources here? What might it
look like?

~~~
hartror
Resource oriented and its implication of nouns as opposed to verbs doesn't
have to equal CRUD. I can see a case for bending the nouns rule of thumb in
this domain, for example I might have an api like so:

    
    
        POST /decks : returns a url to a new deck
        GET  /decks/<id> : returns a deck
        POST /decks/<id>/draw : returns a url to a card
        POST /decks/<id>/shuffle : returns a url to the deck
        GET  /cards/<id> : returns a card drawn from a deck
    

The draw/shuffle verbs arguably could be implemented like this:

    
    
        POST /cards : returns a url to a card (post body having deck id)
        POST /shuffles : returns a url to the deck (post body having deck id)
    

The cards POST makes a lot of sense, now that I look at it I think I would use
that interface. And you could argue that the shuffles resource makes sense as
at some point you may want to record and share when someone shuffles the deck.

~~~
vladharbuz
Some of these don't really make sense.

    
    
        POST /decks/<id>/draw : returns a url to a card
        POST /decks/<id>/shuffle : returns a url to the deck
    

Are you adding a "draw" to deck <id>?

    
    
        POST /cards : returns a url to a card (post body having deck id)
        POST /shuffles : returns a url to the deck (post body having deck id)
    

Are you creating a card or a shuffle?

Here's how things should be, in my opinion:

    
    
        DELETE /deckCard/<deckId>
    

This removes a card from an abstract entity representing the relation between
decks and cards. Thus, it draws a card and returns it, sort of like popping
something off a stack.

    
    
        PUT /decks/<id> with body {shuffle: true}
    

This edits the abstract "shuffle" attribute of deck <id>, shuffling the deck.
This is indeed odd, but I'm afraid it's the best you can do for a mutating
request. I would recommend a non-mutating request that makes a new deck out of
an old deck, perhaps using "source" as an abstract attribute.

    
    
        POST /decks with body {source: <oldId>}
    

It just goes to show that while REST is a good standard for CRUD operations,
and is surprisingly extensible for non-CRUD operations, it can get confusing
and become a real pain. Should one just deal with it or move to e.g. RPC? I
haven't figured this out.

~~~
hartror
Just because a thing, such as a draw or a shuffle isn't a physical thing
doesn't mean it cannot exist as resource. Programming is all about creating
abstractions, so why cannot I create a shuffle or a draw resource? I could
even record them and share them on their own endpoints for clients to view.

Commenting directly on you suggestions:

    
    
        DELETE /deckCard/<deckId>
    

This means each DELETE on this url would result in a different deck state
which isn't idempotent as DELETE is mean to be.

    
    
        PUT /decks/<id> with body {shuffle: true}
    

Same with this, the resource would end up in a new state each time making it
unsafe to do repeatedly as the HTTP spec says.

I think the issue is REST is taught with a CRUD view point and people have
difficulty thinking about it in other ways. Also the English meanings of the
HTTP verbs get confused with their HTTP meanings which doesn't help.

I liked this blog post which talks about the concept of "REST without PUT".

[http://www.thoughtworks.com/insights/blog/rest-api-design-
re...](http://www.thoughtworks.com/insights/blog/rest-api-design-resource-
modeling)

edit: you've edited you comment since I started my reply:

I like your idea of replacing shuffle with a new deck:

    
    
        POST /decks with body {source: <oldId>}
    

You could then do things like lock the source deck which would make it easier
to implement a multi-client system where you cannot draw from a deck until you
have been informed it has been shuffled.

~~~
vladharbuz
I agree with your comments and take them as evidence that REST is exceedingly
difficult and complex for these use cases.

You can create new resources, just as I have, it just doesn't make sense to
"POST /cards" when you're not making a new card at all.

------
flashman
I'm not sure what this file does:
[https://github.com/crobertsbmw/deckofcards/blob/master/list....](https://github.com/crobertsbmw/deckofcards/blob/master/list.txt)

~~~
ctekin
Also unfortunate :
[https://github.com/crobertsbmw/deckofcards/blob/fb02c09b9bc4...](https://github.com/crobertsbmw/deckofcards/blob/fb02c09b9bc48ba8ccaf4a872df8edd650c08cf6/spades/settings.py)

~~~
crobertsbmw
I've changed the secret key that I am using in production. I wanted the
settings.py to be there so it works out of the box if anyone forks it.

------
voltagex_
I wonder if returning SVG would be possible -
[https://code.google.com/p/vectorized-playing-
cards/](https://code.google.com/p/vectorized-playing-cards/)

Edit: Could be an excellent idea - 8 of Clubs is:

* 28kb as a PNG.

* 14kb as an SVG

* 4kb as a gzipped SVG

~~~
Everlag
Thought about that as soon as I saw it returning rasters. SVG would be best
offered alongside as an option; it requires more effort to use than just a
png. Perhaps a 'vector' to accompany each 'image'?

~~~
voltagex_
Accept: headers. Default to image/png

------
soup10
I like the website design but shuffling cards is extremely trivial to code.
Also cards are mostly used for gambling games and it's a huge security hole to
use a third party api for that unless it's from a very trustworthy source.

One maybe useful thing about this api is the card images, but there are also
probably plenty of decks in the public domain.

~~~
nly
> Also cards are mostly used for gambling games and it's a huge security hole
> to use a third party api

Depends. If you're writing a game you wanted the player to trust you might
want to use an external shuffling service so you can easily show proof-of-deck
after the game is over.

Of course, you could do this simply by giving the player a cryptographic hash
of the shuffled deck beforehand, e.g. SHA-256(5H,4D,AS...)

~~~
ivanche
I've worked as a developer for online gambling games for 6 years. We _never_
shuffled the deck of cards. Instead, as player requests another card(s), they
are generated using hardware RNG and returned to player. Deck only knows which
cards are left in it, but their order is undefined until that call to RNG.

~~~
sesquipedalian
If I'm not mistaken, this sounds like an incremental fisher-yates shuffle.

~~~
ivanche
You are correct. As the matter of fact, a bunch of games in majority of cases
needed only the next card from the deck, that's why in our heads we almost
always thought of "not shuffling at all".

------
packetized
Is something new with this project? I believe this same thing was posted 10
days ago.

[https://news.ycombinator.com/item?id=9462184](https://news.ycombinator.com/item?id=9462184)

Would be interesting to hear if some of the issues raised in the previous
submission were fixed or updated.

------
spdustin
Has anyone noticed that it's on Github, and thus it wouldn't be too tough to
submit an Issue or, even, a PR?

~~~
jessaustin
This thread is yet more evidence of the Bikeshed Phenomenon.

~~~
Retra
It's not bike-shedding when there's nothing else to talk about. This is
basically a forum about bike shed painting.

------
javajosh
Ha, nice. Your choice of images is...interesting :)

[https://github.com/crobertsbmw/deckofcards/blob/master/stati...](https://github.com/crobertsbmw/deckofcards/blob/master/static/img/JACKSPADES.png)

~~~
crobertsbmw
I just stole all the images off wikipedia. I wanted to make sure I wouldn't
get sued.

~~~
lectrick
[https://code.google.com/p/vectorized-playing-
cards/](https://code.google.com/p/vectorized-playing-cards/)

------
jwcrux
Neat! Possible typo:

> After two weeks, if actions have been made on the deck then we throw it
> away.

Might be "if no actions"?

~~~
crobertsbmw
fixed. Good catch.

------
xxbondsxx
Pretty cool! Love that theres no authentication and I can start straight from
curl.

Nit: drawing from an empty deck does not throw an error: > curl
[http://deckofcardsapi.com/api/draw/i763hn8lcg0e/](http://deckofcardsapi.com/api/draw/i763hn8lcg0e/)
{"remaining": 0, "cards": [], "deck_id": "i763hn8lcg0e", "success": true}

As does drawing a comically large number of cards: >
[http://deckofcardsapi.com/api/draw/vzlem7q4jhna/?count=10000...](http://deckofcardsapi.com/api/draw/vzlem7q4jhna/?count=100000;)
(...) "success": true}\n

------
jonobird1
I'm just getting a "400 Bad Request" for the home page (from Brisbane,
Australia)

~~~
bt3
Seconded. US here.

~~~
crobertsbmw
Sorry about that. Should be fixed now.

------
evan_
If it supported the idea of multiple players, keeping track of what cards each
player has, and facilitating passing cards between players; it could be a cool
backend for lots of different card games.

~~~
sirclueless
I wonder if you could do most of this cryptographically. If you did, you could
make a client that could do all of this peer-to-peer. I think you would just
need the following to make a wide range of games (many popular ones, perhaps
most?):

1\. Client can produce a signature that it drew the k'th card from the deck
for any k.

2\. Client can produce a signature that the k'th card is X if it drew the k'th
card.

3\. Client can produce a signature that card X came from any arbitrary set of
card indices containing k if it drew the k'th card. (This is the hardest one.)
If it's easier, you could get most of this even restricting it to sets of
cards that the client has drawn.

With just that, I bet you could make a protocol for most card games where no
one can cheat, where the central server's role is only to give out information
about k'th card one and only one time. Certainly it's enough for blackjack,
most forms of poker, hearts, and go fish. Anyone have a game that would be
difficult? Maybe one where hidden cards get passed from player to player.

~~~
lann
You can!
[http://en.m.wikipedia.org/wiki/Mental_poker](http://en.m.wikipedia.org/wiki/Mental_poker)

------
Navarr
There are so many things a good API would have to do...

Different types of decks, handling of a discard, shuffling a card into the
deck, shuffling the discard pile into the deck, handling _hands_ , milling a
card from the deck, multiple decks acting as one...

Though the simplicity of this api currently just having a deck that you take
cards from is neat.

------
adanto6840
What's being used for RNG?

~~~
sirclueless
Appears to be python's random module, so not very secure if that's what you're
after but it's probably correctly pseudorandom:
[https://github.com/crobertsbmw/deckofcards/blob/master/deck/...](https://github.com/crobertsbmw/deckofcards/blob/master/deck/models.py#L33)

------
tjbiddle
Neat! Nice work! Some are definitely criticizing, but hey - you'll always have
critics. Unless they're following through with a pull-request, don't mind
them.

Great job on putting the project out there!

An idea: Multiple options for deck images - obviously would take time on the
artwork, but you could set this in your initial call for a deck (Or maybe even
make it so you can change it whenever - if someone would have a need for that
for some reason). May be fun.

Great work!

~~~
nicobn
The absence of a pull request does not negate the validity of a criticism.
Hacker News is one of the most tech-savvy, intelligent and experienced
audience you can find on the web. Everyone sucks, myself included. With that
kind of attitude, I would never have learned anything.

Shielding yourself from criticism using an arbitrary criteria only perpetuates
ignorance.

------
carrotleads
Sounds straightforward and simple. Well done.

Would like an option to set parameters of a deck. Some card games use only 8
cards from a set and not the whole 13.

------
comrh
Adding and subtracting from the starting deck would very useful. Adding
Jokers, playing with only 1-10 (scopa), ect

------
bloodspatter504
Can someone show or explain:

1\. An example of how this could be used.

2\. How to do something other than just shuffling the cards.

~~~
d0m
I think it was just a fun side project. But as to how it could be used.. just
a few weeks ago we were at a hackathon and built a quick poker texas holdem
game. A library like that (arguably a bit more comprehensive) would have saved
us a few hours.

~~~
MeghdeepRay
Was it just a texas hold em ? Or a variation of it ? I assume being a
hackathon it wasn't a vanilla texas hold em. Could you provide some more
details and maybe the code ? After seeing this I'm interested to know what
else you've done.

~~~
d0m
So we wanted to learn/play with react native. The project idea was to play a
hand of poker online randomly against someone in less than 30 seconds. The fun
part is you can only fold or go all-in : )

------
gabemart
This seems like a really cool project!

But how are you shuffling the cards, and how are you identifying unique
shuffled decks?

It looks like the deck_id is 12 characters, numbers or lower-case letters.
That gives a range of 36^12 or around 4.7 * 10^18. The total range of a
physical deck of shuffled cards is 52! or around 8 * 10^67.

It's possible I have misunderstood what deck_id is for.

~~~
kornish
Looks like deck_id references a persisted model:
[https://github.com/crobertsbmw/deckofcards/blob/master/deck/...](https://github.com/crobertsbmw/deckofcards/blob/master/deck/models.py#L23)

The model stores the state of the deck, including things like order and
remaining cards and how many "decks" of 52 cards are in play.

------
brettkc
Saw something similar demoed at Microsoft's Build event. Looks nice.

------
listic
What's it good for, if it doesn't visualize my cards?

------
drinchev
Wow. I never thought this could be an API. Really good work. I remember I
spent a lot of time developing a class that deals with a deck of cards,
shuffle and associate the card with a png.

I would use this API in the future.

------
blt
OP: what was your motivation for this project?

~~~
crobertsbmw
Thanks for asking. I wanted to build a very simple API, and document it that
my year ago self could understand it and actually figure out how to use the
whole thing. I don't know if this is a good or bad thing, but I spent more
time documenting/designing the single doc page then I did writing the actual
API. I also thought the idea was unique enough that it was worth sharing.

The reason for GET and POST is that it was easier for my year ago self to copy
and paste a GET url into chrome and hit Enter and see what happens.

------
hurin
Seriously, why wouldn't you just use a local RNG? Do you realize it's actually
less work than reading API documentation?

~~~
crobertsbmw
Consider it a bad Rube Goldberg Machine.

------
jaideepsingh
Kudos! Simple and to the point.

------
murbard2
Cute, but an implementation of the mental poker protocol would be far more
valuable.

~~~
crobertsbmw
I think I'll do that next.

------
mattkenefick
Looks good.

------
recursive
HTTP errors as a service

~~~
crobertsbmw
Fixed.

------
leovander

        -people to spam
        -
        -weber@kcnext.com
        -info@cremalab.com
        -andy@lovekc.org
        -info@truckily.com
        -info@secondlifestudios.com
        -help@getflywheel.com
        -hello@elevate.co
        -hello@goodtwin.co
        -Info@ObjectLateral.com
        -editor@siliconprairienews.com
        -info@squareoffs.com
        -josh@socialchangenation.com
        -websupport@psicurity.com
        -sales@rfp365.com
        -travis@rivetcreative.com
        -info@phlyppgear.com
        -kyle@feralfew.com
        -matt@feralfew.com
        -zach@feralfew.com
        -courtney@feralfew.com
        -ben@homesforhackers.com
        -info@cyberjammer.net
        -info@SponsorShout.com
    

[https://github.com/crobertsbmw/deckofcards/commit/2b5675df52...](https://github.com/crobertsbmw/deckofcards/commit/2b5675df526ea146c20bcfcee40b134b8a8bf222)

