Gå till innehåll

Koda statistisk pokeranalys med SQL!


maglub

Recommended Posts

Tjenare,

 

Jag tänkte mig göra en liten statistisk analys av pokerhänder medelst SQL (för er oinvigda är det ett funktionellt språk som används i databaser). Inte för att jag egentligen vet exakt hur man gör det på bästa sätt, utan det hoppas jag andra ADB-intresserade pokerspelare kan hjälpa mig med.

 

Det jag tänkte börja med är att visa hur man i MySQL enkelt kan generera ett par tabeller, som vi kan använda som utgångspunkt, och ett par enka utsökningar för att representera lite olika händer.

 

Det är ganska många olika saker att hålla reda på samtidigt, men jag tror att vi gemensamt kan göra något bra av det hela.

 

Att skapa de tabellerna jag rekommenderar i mitt inlägg kommer kräva några hundra MB på din hårddisk.

 

MySQL är gratis och hämtas enkelt ner från http://www.mysql.com och kan installeras på Windows, Linux och lite annat smått och gått.

 

Jag tycker själv inte alls om MySQL, men använder den för att den är enkel att installera för er andra och den är ganska populär i den så kallade akademiska världen.

 

Jag vill heller inte påstå att jag är någon självutnämnd expert varesig inom statistik, sannolikhetslära eller ADB, utan vill lära mig lite grand och hoppas på hjälp från likasinnade.

 

Som grund för att kunna hänga med här måste du själv ladda ner MySQL och installera programvaran. Sedan måste du öppna MySQL command line client. Om du inte fixar det, fråga gärna inte mig hur man gör. Jag kommer ändå inte riktigt ha tid att hjälpa till. Det finns hur mycket hjälp att få med det på google.

 

Över till själva databasen. Först måste vi ha en databas. Den skapar man enklast så här:

 

drop database poker;
create database poker;
use poker;

 

Så där, då är databasen skapad. Ingenting ligger i den, utan den är bara förberedd så vi kan göra något med den.

 

Det vi sedan behöver är en kortlek. Den tillverkar man enkelt genom att först skapa en tabell och sedan lägga in kort för kort i den:

 

drop table kortlek;
create table kortlek (
 id int,
 suite text,
 value int );

-- Hjärter
insert into kortlek values (  1,  'H',  1);
insert into kortlek values (  2,  'H',  2);
insert into kortlek values (  3,  'H',  3);
insert into kortlek values (  4,  'H',  4);
insert into kortlek values (  5,  'H',  5);
insert into kortlek values (  6,  'H',  6);
insert into kortlek values (  7,  'H',  7);
insert into kortlek values (  8,  'H',  8);
insert into kortlek values (  9,  'H',  9);
insert into kortlek values ( 10,  'H', 10);
insert into kortlek values ( 11,  'H', 11);
insert into kortlek values ( 12,  'H', 12);
insert into kortlek values ( 13,  'H', 13);

-- Ruter
insert into kortlek values ( 14,  'R',  1);
insert into kortlek values ( 15,  'R',  2);
insert into kortlek values ( 16,  'R',  3);
insert into kortlek values ( 17,  'R',  4);
insert into kortlek values ( 18,  'R',  5);
insert into kortlek values ( 19,  'R',  6);
insert into kortlek values ( 20,  'R',  7);
insert into kortlek values ( 21,  'R',  8);
insert into kortlek values ( 22,  'R',  9);
insert into kortlek values ( 23,  'R', 10);
insert into kortlek values ( 24,  'R', 11);
insert into kortlek values ( 25,  'R', 12);
insert into kortlek values ( 26,  'R', 13);

-- Klöver
insert into kortlek values ( 27,  'K',  1);
insert into kortlek values ( 28,  'K',  2);
insert into kortlek values ( 29,  'K',  3);
insert into kortlek values ( 30,  'K',  4);
insert into kortlek values ( 31,  'K',  5);
insert into kortlek values ( 32,  'K',  6);
insert into kortlek values ( 33,  'K',  7);
insert into kortlek values ( 34,  'K',  8);
insert into kortlek values ( 35,  'K',  9);
insert into kortlek values ( 36,  'K', 10);
insert into kortlek values ( 37,  'K', 11);
insert into kortlek values ( 38,  'K', 12);
insert into kortlek values ( 39,  'K', 13);

-- Spader
insert into kortlek values ( 40,  'S',  1);
insert into kortlek values ( 41,  'S',  2);
insert into kortlek values ( 42,  'S',  3);
insert into kortlek values ( 43,  'S',  4);
insert into kortlek values ( 44,  'S',  5);
insert into kortlek values ( 45,  'S',  6);
insert into kortlek values ( 46,  'S',  7);
insert into kortlek values ( 47,  'S',  8);
insert into kortlek values ( 48,  'S',  9);
insert into kortlek values ( 49,  'S', 10);
insert into kortlek values ( 50,  'S', 11);
insert into kortlek values ( 51,  'S', 12);
insert into kortlek values ( 52,  'S', 13);

commit;

 

Nu har vi en kortlek, från vilken vi kan "dra" kort. Man kan fråga databasen om en massa ointressanta saker. Exempelvis, hur många hjärter finns det i leken?

 

mysql> select count(*) from kortlek where suite='H';
+----------+
| count(*) |
+----------+
|       13 |
+----------+
1 row in set (0.00 sec)

 

Nu avslutar jag mitt första inlägg och fortsätter lite mer i ett nytt inlägg.

 

Hoppas någon mer hänger på.

 

//magnus

Länk till kommentar
Dela på andra webbplatser

Tjenare igen,

 

Det är ju inte så jätteintressant att bara ha en kortlek, utan man vill ju gärna göra någonting med den.

 

Här har ni ett par intressanta utsökningar att leka med:

 

De första två korten. Hur många olika händer finns det?

 

 select a.id as k1, b.id as k2
 FROM kortlek a, kortlek b
 WHERE
          a.id <> b.id
      and a.id <  b.id;

 

Jag kollar i denna utsökning inte alls efter vilken valör eller färg som finns. Det enda jag kollar är att det andra kortet inte är samma som det första, samt att jag ser till att jag inte både får handen {1,2} och {2,1}.

 

Sammanlagt returneras alltså 1326 rader i denna utsökningen. Men vän av ordning vet ju att det inte finns så många "unika" händer. Klöver Ess i kombination med Hjärter Kung är lika mycket värt som Ruter Ess i kombination med Ruter Kung.

 

Detta kan man undersöka med följande utsökning, där man helt enkelt måste ta hänsyn till färg och valör på korten:

 

-- första handen när olika färger är lika värda

select a.id as k1, b.id as k2, a.suite, a.value,  b.suite, b.value
 FROM kortlek a, kortlek b
 WHERE
          a.id <> b.id
      and a.id <  b.id
 group by a.value, b.value;

 

Med den utsökningen returneras 169 starthänder. På skärmen ser det ut så här:

 

mysql> select a.id as k1, b.id as k2, a.suite, a.value,  b.suite, b.value
   ->   FROM kortlek a, kortlek b
   ->   WHERE
   ->            a.id <> b.id
   ->        and a.id <  b.id
   ->   group by a.value, b.value;
+------+------+-------+-------+-------+-------+
| k1   | k2   | suite | value | suite | value |
+------+------+-------+-------+-------+-------+
|    1 |   14 | H     |     1 | R     |     1 |
|    1 |    2 | H     |     1 | H     |     2 |
|    1 |    3 | H     |     1 | H     |     3 |
|    1 |    4 | H     |     1 | H     |     4 |
|    1 |    5 | H     |     1 | H     |     5 |
...
|   13 |   25 | H     |    13 | R     |    12 |
|   13 |   26 | H     |    13 | R     |    13 |
+------+------+-------+-------+-------+-------+
169 rows in set (0.03 sec)

 

Nu börjar det röra lite på sig.

 

//magnus

edit: ändrade utsökningarna så de fungerar även i postgresql

Länk till kommentar
Dela på andra webbplatser

Och som en liten sista gottebit, vill jag ge er ett par lite mer saftiga utsökningar. Först skapar vi en ny tabell, som får representera alla möjliga kombinationer som kan ses på bordet efter "river".

 

-- alla möjliga kombinationer på bordet
create table bordet as

select a.id k1, b.id k2, c.id k3, d.id k4, e.id k5
from kortlek a, kortlek b, kortlek c, kortlek d, kortlek e
WHERE
        a.id <> b.id
    and a.id <> c.id
    and a.id <> d.id
    and a.id <> e.id
    and b.id <> c.id
    and b.id <> d.id
    and b.id <> e.id
    and c.id <> d.id
    and c.id <> e.id
    and d.id <> e.id

    and a.id <  b.id
    and b.id <  c.id
    and c.id <  d.id
    and d.id <  e.id;

 

Denna tabell är ganska intressant, då man ur den kan söka ut alla möjliga bordkombinationer mot en tvåkortshand. Tabellen blir ca 2.6 Miljoner rader lång och kommer ta upp ca 200 MB på hårddisken. Dock är det mycket snabbare att skapa denna tabeller i förhand än att göra en komplex utsökning varje gång man vill analysera innehållet (eller skapa en så kallad "vy" i databasen).

 

Exempelvis kan man med en enkel utsökning se att för varje hand man har, finns det ca 2.1 Miljoner olika bordkombinationer. Vi använder oss av en godtycklig hand (kort nummer 1 och 2), som får representera "vilken hand som helst".

 

select count(*) from flop
where
        k1 not in (1,2)
    and k2 not in (1,2)
    and k3 not in (1,2)
    and k4 not in (1,2)
    and k5 not in (1,2);

 

Utsökningen tar ca 8.91 sekunder på min laptop och ger följande resultat:

 

mysql> select count(*) from flop
   -> where
   ->          k1 not in (1,2)
   ->      and k2 not in (1,2)
   ->      and k3 not in (1,2)
   ->      and k4 not in (1,2)
   ->      and k5 not in (1,2);
+----------+
| count(*) |
+----------+
|  2118760 |
+----------+
1 row in set (8.91 sec)

 

Någon annan som har något intressant att tillägga?

 

Det jag skulle vilja göra, är att för varje möjlig starthand (alla 1326 eller 169 starthänder) göra en inbördes rangordning, hur bra den är, alltså hur många av de andra (1325 eller 168) händerna som rent kombinatoriskt är sämre.

 

Det kommer kräva ganska mycket datakapacitet att göra denna analys, samt ganska mycket tankeverksamhet - hur man jämför en hand mot en annan, då man måste sätta poäng på handen (ett par är sämre än två par...).

 

Det kan bli ett intressant litet projekt det här. Speciellt om det finns fler som skulle kunna vara intresserade.

 

Ha det gott!

//magnus

Länk till kommentar
Dela på andra webbplatser

Vetgirig: Tackar för kommentaren. Jag tror du har rätt, om man är ute efter att göra tabellen "safe". I det här exemplet tror jag vi vill ha en så kompakt tabell (liten i storlek) som möjligt, samt kanske optimera i datatyper som kräver så lite datakraft som möjligt att läsa.

 

Exempelvis, spara kollumnen suite som char istället...

 

Men just kortleken är inte det som är kritiskt, tror jag, då mina "följd"-tabeller för tillfället inte använder sig av annat än integers för att peka vilket kort i leken som refereras till. Eventuellt vill vi aggregera den informationen in i en mer lättsökt (läs utan joins) tabell i något typexempel senare.

 

//magnus

Länk till kommentar
Dela på andra webbplatser

Vetgirig: Tackar för kommentaren. Jag tror du har rätt, om man är ute efter att göra tabellen "safe". I det här exemplet tror jag vi vill ha en så kompakt tabell (liten i storlek) som möjligt, samt kanske optimera i datatyper som kräver så lite datakraft som möjligt att läsa.

 

Exempelvis, spara kollumnen suite som char istället...

 

//magnus

 

 

För enumen så kostar det 1 bytes medan för din TEXT kostar det 3 bytes och INT 4 bytes så att använda enum borde minska lagringstorleken från 7 bytes till 2 per kort.

 

Så om du använder enum för valör och färg så minskar lagringsbehovet till 28% av din lösning.

Länk till kommentar
Dela på andra webbplatser

Undrar lite varför du använder en databas men det kanske ger sig med tiden.

Att göra ett program som håller kortleken i minnet och utför de beräkningar du gjort skulle gå +1000ggr snabbare.

 

Blir ju mkt mer dynamiskt än att behöva skriva nya saker till ett program varje gång man vill testa sig fram till något nytt.

 

Dock kan jag rekommendera ett färdigt API på: http://sourceforge.net/projects/pokersource/

Länk till kommentar
Dela på andra webbplatser

Gott, tack for lanken!

 

En av anledningarna till att jag vill gora denna studie i SQL, ar att jag daa och daa har tillgang till vaeldigt kraftfulla prylar, stora datorer och professionell lagring, vi snackar terabyte med snabb storage, upp till en eller ett par gigabyte per sekund.

 

Ibland, inte alltid, kan man kora lite funktionstester paa egen hand.

 

Och just naer man kan bygga en "kub" med ett gaeng haender och faa ut en analys om hur bra just den handen aer, paa ett saett som kraever mycket stort internminne eller stor diskcache, saa aer det smidigt med just SQL och en databas.

 

//magnus

Länk till kommentar
Dela på andra webbplatser

Ok, har lite tråkigt i dag, även om jag egentligen inte har tid. Här är en utsökning, som visar hur många första två kort som är suited:

 

-- suited i given
select count(*)
 FROM kortlek a, kortlek b
 WHERE
          a.id <> b.id
      and a.id <  b.id
      and a.suite = b.suite;

count
-------
  312
(1 row)

 

Dela det med 1326 (antaled första två händer), så ser ni att det är 23.5% sannolikhet att få suited i given. Lätt att räkna med kombinatorik också:

 

Första kortet är egalt: (52/52)

Andra kortet skall vara lika: (12/51)

 

(52/52)*(12/51)=23.5%

 

//magnus

Länk till kommentar
Dela på andra webbplatser

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Gäst
Svara i detta ämne...

×   Du har klistrat in innehåll med formatering.   Ta bort formatering

  Endast 75 max uttryckssymboler är tillåtna.

×   Din länk har automatiskt bäddats in.   Visa som länk istället

×   Ditt tidigare innehåll har återställts.   Rensa redigerare

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Skapa nytt...