5
1

Here's a crazy example that's been on my mind lately. Imagine that we've expressed a poker hand in RDF. I'm going to be flexible as to exactly how it's done, but an RDF list would work.

 :myHand :isPokerHand (
     [] :value :King ; :suit :Spades.
     [] :value :King ; :suit :Hearts.
     [] :value :Ace ; :suit :Clubs.
     [] :value :Seven ; :suit :Hearts .
     [] :value :Eight ; :suit :Diamonds .
 ) .

in this case, I want to infer

 :myHand a :Pair .

I'd like to have types that correspond to the types of hands that occur in poker.

 :myHead :beats :someOtherHand .

if :someOtherHand is, say, a pair of :Queen(s). I'm happy to see any method of inference used, and as mentioned before, if you can do it with a :Bag or using a property to represent hand membership, that's find with me too. Of course, a complete working system is a lot to ask, but a rough design would count towards a useful answer

asked 14 Oct '11, 19:59

database_animal's gravatar image

database_animal ♦
8.4k1612
accept rate: 15%

edited 20 Oct '11, 08:45

Antoine%20Zimmermann's gravatar image

Antoine Zimm... ♦
10.5k514


It's an absolute cinch with OWL 2:

Semantics of the cards first:

:HeartCard :equivalentClass [ :oneOf ( :2H :3H ... :KH :AH) ] , [ owl:hasValue :Hearts ; owl:onProperty :suit ] [ owl:hasValue :magic ; owl:onProperty :heart ] .
:SpadeCard ...
:ClubCard ...
:DiamondCard ...
:2Card :equivalentClass [ :oneOf ( :2H :2S 2C :2D ) ] , [ owl:hasValue :2 ; owl:onProperty :suit ] ; :next :3Card .
:2 :nextValue :3 .
...
:ACard ...
:A ...
:Card owl:equivalentClass [ owl:unionOf ( :HeartCard :SpadeCard :ClubCard :DiamondCard ) ] , [ owl:unionOf ( :2Card ... :ACard ) ] ; owl:hasKey ( :suit :value ) .
[ a owl:AllDifferentFrom ; owl:members ( :2H :3H ... :KD :AD ) ] .
:2H a :2Card , :HeartCard .
...
:AD a ...

A little glue:

:heart rdfs:subPropertyOf :notSpade , :notClub , :notDiamond .
:spade ...
:notHeart owl:inverseOf :heartNot .
...
:next rdfs:subPropertyOf :lessThan ; owl:propertyChainAxiom ( :hasValue :nextValue ) .
:equal a owl:ReflexiveProperty .
:equal rdfs:subPropertyOf :lessThanEqual . :lessThan rdfs:subPropertyOf :lessThanEqual .
:lessThan a owl:TransitiveProperty ; owl:IrreflexiveProperty ; owl:inverseOf :greaterThan .
:sameHandNext ~todo, possible?~
:sameHandLessThan ...
:straightCheck owl:propertyChainAxiom ( :hasCard :sameHandNext :sameHandNext :sameHandNext :sameHandNext ) .
:suit owl:inverseOf :suitHas .
:differentSuit owl:propertyChainAxiom ( :heart :heartNot ) , ( :spade :spadeNot ) ; owl:propertyDisjointWith :sameSuit .
:sameSuit owl:propertyChainAxiom ( :suit :suitHas ) .
:negFlushCheck owl:propertyChainAxiom ( :hasCard :sameHandDifferentSuit ) .
:sameSuitDifferentCard ~todo; full enumeration?~
:sameValueDifferentCard ~todo; full enumeration?~
:highestCard owl:propertyChainAxiom ( :hasCard :sameHandlessThanEqual :sameHandlessThanEqual :sameHandlessThanEqual :sameHandlessThanEqual ) ; owl:inverseOf :highestCardOf .
:2pair3OfAKindCheck owl:propertyChainAxiom ( :sameHandLessThan :sameHandLessThan :highestCardOf  ) .

...and then you're ready to define some semantics to classify different hands:

:PokerHand rdfs:subClassOf [ owl:cardinality 5 ; owl:onProperty :hasCard ] .
:FlushHand [ owl:intersectionOf ( :PokerHand [ owl:complementOf [ owl:someValuesFrom owl:Thing ; owl:onProperty :negFlushCheck ] ] ) ] .
:StraightHand owl:intersectionOf  ( :PokerHand ; [ owl:someValuesFrom :Card ; owl:onProperty :straightCheck ]  ) .
:StraightFlushHand owl:intersectionOf ( :FlushHand :StraightHand ) .
:StrictStraightHand owl:intersectionOf ( :StraightHand [ owl:complementOf :StraightFlushHand ] ) .
:StrictFlushHand owl:intersectionOf ( :FlushHand [ owl:complementOf :StraightFlushHand ] ) .
:RoyaleFlushHand owl:intersectionOf ( :StraightFlushHand [ owl:someValuesFrom :ACard ; owl:onProperty :hasCard ] ) .
:4OfAKindHand owl:intersectionOf ( :PokerHand [ owl:unionOf ( [ owl:qualifiedCardinality 4 ; owl:onProperty :hasCard ; owl:onClass :2Card ] ... [ owl:qualifiedCardinality 4 ; owl:onProperty :hasCard ; owl:onClass :ACard ] ) ] ) .
:3OfAKindHand owl:intersectionOf ( ... ) .
:2OfAKindHand owl:intersectionOf ( ... ) .
:FullHouseHand owl:intersectionOf ( :3OfAKind :2OfAKind ) .
:2PairHand owl:intersectionOf ( :PokerHand [ owl:intersectionOf ( [ owl:someValuesFrom owl:Thing ; owl:onProperty :2pair3OfAKindCheck ] [ owl:complementOf :3OfAKind ] ) ] )
:Strict3OfAKindHand owl:intersectionOf ( :3OfAKind [ owl:complementOf :FullHouseHand ] ) .
:Strict2OfAKindHand owl:intersectionOf ( :2OfAKind [ owl:complementOf [ owl:unionOf ( :FullHouseHand :2PairHand  ) ] ] ) .
:StrictHighCard owl:intersectionOf ( :PokerHand [ owl:complementOf ~everythingAbove ] ) .
:6HighCard owl:intersectionOf ( :PokerHand [ owl:someValuesFrom :6Card ; owl:onProperty :highCard ]  ) .
...
:Strict7HighCard owl:intersectionOf ( :StrictHighCard [ owl:someValuesFrom :7Card ; owl:onProperty :highCard ] )
...

And then defining the ordering of different (strict) hands is a piece of cake using the principle of "All Elephants are Bigger Than All Mice"... in this case, all StraightFlushs beat all FourOfAKinds, etc., all 7Highs valueBeat all AHighs, etc., then chain beatsameHandTypevalueBeat and drawsameHandTypesameValue.

...see, it's a cinch.

permanent link

answered 16 Oct '11, 23:25

Signified's gravatar image

Signified ♦
24.0k1623
accept rate: 37%

edited 20 Oct '11, 18:06

1

</sarcasm> ;)

(16 Oct '11, 23:28) Signified ♦ Signified's gravatar image
2

+1 for brazen sarcasm

(17 Oct '11, 05:56) Rob Vesse ♦ Rob%20Vesse's gravatar image
1

:beats is a little more complicated than that... A pair of Jacks beat a pair of Sevens, for instance. However, that's a great answer!

(17 Oct '11, 11:22) database_animal ♦ database_animal's gravatar image
1

And for all who consider doing real reasoning based on this with their favorite OWL reasoner, let me tell you that OWL inferencing can easily become something like a Poker game! ;-)

(17 Oct '11, 11:32) Michael Schn... ♦ Michael%20Schneider's gravatar image

...ack, true yep. High card doesn't cut it, but a little bit more axiom-wrangling to define high-pairs, high-trips and high-quads and you'd get there (eventually).

There's no doubt a few bugs in the above sketch, and shortcuts might be possible, but given a week or so I'm fairly sure a full solution is feasible, at least in OWL 2 Full. The one concern I'd have is the definition of :sameHandNext which requires role intersections not supported in OWL, but there should be a way around it using :hasSelf.

(17 Oct '11, 11:39) Signified ♦ Signified's gravatar image

...a more practical problem would be finding a reasoner smart enough to classify hands. (Sticking to OWL 2 DL would be very beneficial here.... otherwise maybe @Michael might have a solution... http://www.springerlink.com/content/2x4p65l6k4pv6885/)

(17 Oct '11, 11:40) Signified ♦ Signified's gravatar image

Maybe! :-) Fact is, that I haven't applied this OWL Full implementation approach to such a complex "real-world" application so far. ;-) Maybe I'll find some time to apply the approach to your axiomatization (after its completion, and after ISWC, I'm too busy now).

Man, automatic reasoning for Poker gaming... I guess, that's where the money goes, eventually! ;-)

(17 Oct '11, 12:35) Michael Schn... ♦ Michael%20Schneider's gravatar image
1

...was actually wondering last night if doing a full ontology and classifier (using an existing reasoner) for poker hands in OWL would be publishable... might be a bit of fun... who's in? :)

(18 Oct '11, 10:06) Signified ♦ Signified's gravatar image
3

Could become a classic representer of the category "problems for which there are tons of solutions using traditional software development approaches which can also be solved via sophisticated Semantic Web reasoning". ;-)

(18 Oct '11, 12:58) Michael Schn... ♦ Michael%20Schneider's gravatar image

@Michael I'm interested in this because I want to replace a mess of internal and external DSLs with something that's standardized.

I might to easily change my Poker A.I. to play 'Dealers Choice' or update a system to next year's tax code... The thing I do know is that RDF data is going into the system, so I need some powerful way to express rules over it.

(20 Oct '11, 17:02) database_animal ♦ database_animal's gravatar image
showing 5 of 10 show 5 more comments

Ok so here's my attempt at doing this with just RDF and SPARQL.

Here's my sample data:

@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
@prefix : <http://example.org/poker/>.

:Ace a :Card .
:King a :Card ;
      :beatenBy :Ace .
:Queen a :Card ; 
        :beatenBy :King .
:Jack a :Card ;
      :beatenBy :Queen .
:Ten a :Card ;
     :beatenBy :Jack .
:Nine a :Card ;
      :beatenBy :Ten .
:Eight a :Card ;
        :beatenBy :Nine .
:Seven a :Card ;
        :beatenBy :Eight .
:Six a :Card ;
     :beatenBy :Seven .
:Five a :Card ;
      :beatenBy :Six .
:Four a :Card ;
      :beatenBy :Five .
:Three a :Card ;
       :beatenBy :Four .
:Two a :Card ;
      :beatenBy :Three .

:me :hasPokerHand [ :containsCard [ :value :King ; :suit :Hearts ] ,
                                     [ :value :King ; :suit :Spades ] ,
                                     [ :value :King ; :suit :Clubs ],
                                     [ :value :Three ; :suit :Diamonds ],
                                     [ :value :Seven ; :suit :Hearts ] ] .

:opponent :hasPokerHand [ :containsCard [ :value :Ace ; :suit :Diamonds ],
                                            [ :value :Four ; :suit :Clubs ],
                                            [ :value :Ace ; :suit :Spades ],
                                            [ :value :Ace ; :suit :Hearts ],
                                            [ :value :Nine ; :suit :Clubs ] ] .

:opponent2 :hasPokerHand [ :containsCard [ :value :Seven ; :suit :Clubs ],
                                             [ :value :Eight ; :suit :Clubs ],
                                             [ :value :Two ; :suit :Clubs ],
                                             [ :value :Jack ; :suit :Clubs ],
                                             [ :value :Ace ; :suit :Clubs ] ] .

:opponent3 :hasPokerHand [ :containsCard [ :value :Three ; :suit :Spades ],
                                             [ :value :Four ; :suit :Spades ],
                                             [ :value :Five ; :suit :Spades ],
                                             [ :value :Six ; :suit :Spades ],
                                             [ :value :Seven ; :suit :Spades ] ] .

Firstly checking we have a valid hand:

PREFIX : <http://example.org/poker/>

SELECT 
  ?player 
  (COUNT(?card) AS ?cards) 
  (IF(?cards = 5, true, false) AS ?ValidHand) 
WHERE 
{
  ?player :hasPokerHand ?hand .
  ?hand :containsCard ?card .
} GROUP BY ?player

We can use SELECT queries to determine what hand you have e.g. pairs, three of a kind or four of a kind

PREFIX : <http://example.org/poker/>

SELECT 
  ?player
  ?value 
  (COUNT(?value) AS ?count) 
  (IF(?count = 2, true, false) AS ?Pair) 
  (IF(?count = 3, true, false) AS ?ThreeOfAKind)
  (IF(?count = 4, true, false) AS ?FourOfAKind)
WHERE
{
  ?player :hasPokerHand ?hand .
  ?hand :containsCard ?card .
  ?card :value ?value
} GROUP BY ?player ?value HAVING (COUNT(?value) > 1)

Full house starts to get a bit more complicated since we're looking for a pair and a 3 of a kind. We can do this by using the previous query as a subquery and then using an IF to determine if this is present:

PREFIX : <http://example.org/poker/>

SELECT
  ?player
  IF (?ThreeOfAKind && ?Pair, true, false) AS ?FullHouse)
WHERE
{
  {
    SELECT 
      ?player
      ?value 
      (COUNT(?value) AS ?count) 
      (IF(?count = 2, true, false) AS ?Pair) 
      (IF(?count = 3, true, false) AS ?ThreeOfAKind)
      (IF(?count = 4, true, false) AS ?FourOfAKind)
    {
      ?player :hasPokerHand ?hand .
      ?hand :containsCard ?card .
      ?card :value ?value
    } GROUP BY ?player ?value HAVING (COUNT(?value) > 1)
  }
}

Flushes are relatively simple:

PREFIX : <http://example.org/poker/>

SELECT
  ?player
  ?suit
  (true AS ?Flush)
WHERE
{
  ?player :hasPokerHand ?hand .
  ?hand :containsCard ?card .
  ?card :value ?value ;
        :suit ?suit .
} GROUP BY ?player ?suit HAVING (COUNT(?value) = 5)

Straights are again a bit harder, note that the desired count is 4 and not 5 since the highest card in the straight will not have a card above it in the hand:

PREFIX : <http://example.org/poker>

SELECT
  ?player
  (true AS ?Straight)  
WHERE
{
  ?player :hasPokerHand ?hand .
  ?hand :containsCard ?card .
  ?card :value ?value .
  BIND(EXISTS { 
              ?value :beatenBy ?higherValue . 
              ?hand :containsCard ?higherCard . 
              ?higherCard :value ?higherValue
            } AS ?StraightMember)
  FILTER(?StraightMember)
} GROUP BY ?player HAVING (COUNT(?StraightMember) = 4)

From this you could work up to queries that determine whose hand is better though I don't have time to do this right now, may add more later when I have more time...

permanent link

answered 17 Oct '11, 06:44

Rob%20Vesse's gravatar image

Rob Vesse ♦
14.0k1715
accept rate: 29%

Your answer
toggle preview

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Markdown Basics

  • *italic* or _italic_
  • **bold** or __bold__
  • link:[text](http://url.com/ "title")
  • image?![alt text](/path/img.jpg "title")
  • numbered list: 1. Foo 2. Bar
  • to add a line break simply add two spaces to where you would like the new line to be.
  • basic HTML tags are also supported

Question tags:

×115
×71
×10
×1
×1

question asked: 14 Oct '11, 19:59

question was seen: 2,012 times

last updated: 20 Oct '11, 18:06