JavaScriptでポーカー
JavaScriptでポーカーを作りました。
最初はコンピューターと対戦できるようにしたかったんですが、
まともに勝負できるAIを考えるのが果てしなく面倒そうなので、
ポーカーのシミュレーターにしました。
実行ボタンで開始します。
(画像読み込みの間、表示されないトランプがでるかもしれないです。)
内部処理としては、現実の
揃えられたトランプ→シャッフル→上から5枚引く→役チェック→
カードをすべて戻す→シャッフル→上から5枚引く→役チェック…
ということを繰り返していることになります。
役は以下のように判定しています。
一番正直な方法をそのまま実装した感じです。
Poker.checkHand = function(cards) { var flush = 1; for(var i = 1; i < 5; ++i){ if(cards[0].suit != cards[i].suit){ flush = 0; break; } } var pair = 0; for(var i = 0; i < 4; ++i){ for(var j = i+1; j < 5; ++j){ if(cards[i].number == cards[j].number) ++pair; } } cards.sort(function(a, b){ return (a.number > b.number) ? 1 : -1; }); var straight = 1; for(var i = 1; i < 5; ++i){ if(cards[i-1].number + 1 != cards[i].number){ straight = 0; break; } } if(cards[0].number == 1 && cards[1].number == 10 && cards[2].number == 11 && cards[3].number == 12 && cards[4].number == 13){ straight = 2; } return (flush * 100 + straight * 10 + pair); }
ポーカーの役判定コードとしてはどう書く?orgに、すごくスマートなコードが上がっていたのですが、スマートすぎて何をやってるのかイマイチわかりませんでした…。
今のところ20ミリ秒ごとに32回チェックを繰り返すように設定しています。
多分、もうちょっと増やしても大丈夫だと思うのですが、
あまりやりすぎてブラウザがフリーズしても困るので…。
動かしてみると、最初引いたときの状態は、ノーペア(役なし)になる確率が50%、ワンペアになる確率が42%、ツーペアになる確率が4%…。
ということがわかって面白いですね。
ロイヤルストレートフラッシュくらいになると、ほぼ0%です。
イカサマ以外でこれが出るとしたら奇跡ですね。
こういうのを知っていると現実にポーカーをやった時に、
「ノーペアになる確率が50%、ワンペアになる確率が42%…(メガネクイッ」
って出来るのでカッコいい…ことはないですが、何かの役に立つかもしれません。
ソースコードは以下においておきます。いつもよりは短めです。
// 一枚のカード var Card = function(id,suit,number) { this.x = 0; this.y = 0; this.suit = suit; this.number = number; this.name = id + "" + this.suit + "" + this.number; var url = "image/" + this.suit + "" + this.number + ".png"; document.write('<img id="'+this.name+'" src="'+url+'" STYLE="position:absolute;top:'+this.y+';left:'+this.x+';visibility:hidden">'); } Card.prototype.put = function(x,y) { var element = document.getElementById(this.name); element.style.left = x; element.style.top = y; element.style.visibility = "visible"; } Card.prototype.hidden = function() { document.getElementById(this.name).style.visibility = "hidden"; } Card.SUIT_SPADE = 0; Card.SUIT_DIA = 1; Card.SUIT_HEART = 2; Card.SUIT_CLUB = 3; Card.SUIT_NONE = 4; // 1組52枚のトランプ var Trump = function() { this.cards = new Array(); for(var i = 0; i < 4; ++i){ for(var j = 1; j < 14; ++j){ this.cards.push(new Card(this.instanceCount.length,i,j)); } } this.cards.reverse(); this.instanceCount.push(this); } Trump.prototype.instanceCount = new Array(); // 一番上から一枚引きます。 Trump.prototype.drawTop = function() { return this.cards.pop(); } // 一番上にカードを戻します。 Trump.prototype.returnTop = function(card) { this.cards.push(card); } // トランプをシャッフルします。 // Fisher-Yates // http://la.ma.la/blog/diary_200608300350.htm Trump.prototype.shuffle = function() { var i = this.cards.length; while(i){ var j = Math.floor(Math.random()*i); var t = this.cards[--i]; this.cards[i] = this.cards[j]; this.cards[j] = t; } return this.cards; } var Poker = function() { } // 役を判定します。 Poker.checkHand = function(cards) { var flush = 1; for(var i = 1; i < 5; ++i){ if(cards[0].suit != cards[i].suit){ flush = 0; break; } } var pair = 0; for(var i = 0; i < 4; ++i){ for(var j = i+1; j < 5; ++j){ if(cards[i].number == cards[j].number) ++pair; } } cards.sort(function(a, b){ return (a.number > b.number) ? 1 : -1; }); var straight = 1; for(var i = 1; i < 5; ++i){ if(cards[i-1].number + 1 != cards[i].number){ straight = 0; break; } } if(cards[0].number == 1 && cards[1].number == 10 && cards[2].number == 11 && cards[3].number == 12 && cards[4].number == 13){ straight = 2; } return (flush * 100 + straight * 10 + pair); } // 役定数 Poker.HD_ROYAL_FLUSH = 120; Poker.HD_STRAIGHT_FLUSH = 110; Poker.HD_FLUSH = 100; Poker.HD_ROYAL_STRAIGHT = 20; Poker.HD_STRAIGHT = 10; Poker.HD_FOUR_OF_A_KIND = 6; Poker.HD_FULL_HOUSE = 4; Poker.HD_THREE_OF_A_KIND = 3; Poker.HD_TWO_PAIR = 2; Poker.HD_ONE_PAIR = 1; Poker.HD_NO_PAIR = 0; // checkHandの戻り値を役の名前に変換します。 Poker.codeToName = function(code) { switch(code){ case Poker.HD_NO_PAIR: return "No pair"; case Poker.HD_ONE_PAIR: return "One pair"; case Poker.HD_TWO_PAIR: return "Two pair"; case Poker.HD_THREE_OF_A_KIND: return "Three of a kind"; case Poker.HD_STRAIGHT: return "Straight"; case Poker.HD_ROYAL_STRAIGHT: return "Straight"; case Poker.HD_FLUSH: return "Flush"; case Poker.HD_FULL_HOUSE: return "Full house"; case Poker.HD_FOUR_OF_A_KIND: return "Four of a kind"; case Poker.HD_STRAIGHT_FLUSH: return "Straight flush"; case Poker.HD_ROYAL_FLUSH: return "Royal flush"; } return undefined; } // checkHandの戻り値を役の強さに変換します。 Poker.codeToRank = function(code) { switch(code){ case Poker.HD_NO_PAIR: return 0; case Poker.HD_ONE_PAIR: return 1; case Poker.HD_TWO_PAIR: return 2; case Poker.HD_THREE_OF_A_KIND: return 3; case Poker.HD_STRAIGHT: return 4; case Poker.HD_ROYAL_STRAIGHT: return 4; case Poker.HD_FLUSH: return 5; case Poker.HD_FULL_HOUSE: return 6; case Poker.HD_FOUR_OF_A_KIND: return 7; case Poker.HD_STRAIGHT_FLUSH: return 8; case Poker.HD_ROYAL_FLUSH: return 9; } return undefined; } var nameList = ["ノーペア","1ペア","2ペア","3カード","ストレート","フラッシュ","フルハウス","4カード","ストレートフラッシュ","ロイヤルストレートフラッシュ"]; var handList = new Array(10); var allCount = 0; for(var i = 0; i < handList.length; ++i){ handList[i] = 0; } document.write('<b><font size="4" id="hand"></font></b></br>'); document.write('<font size="1" id="alls"></font></br>'); document.write('\ <div id="list">\ '+setList()+'\ </div>\ '); document.write('<input type="button" value="STOP" onclick="press(this)">'); var trump = new Trump(); trump.shuffle(); var player = new Array(); show(); var waitCount = 0; var timer = start(); function start() { var timer = setInterval(function(){ if(--waitCount <= 0){ waitCount = 0; for(var i = 0; i < 32; ++i){ if(play()) break; } } },20); return timer; } function press(button) { if(timer == 0){ timer = start(); button.value = "STOP"; }else{ clearInterval(timer); timer = 0; button.value = "START"; } } function show() { ++allCount; for(var i = 0; i < 5; ++i){ player[i] = trump.drawTop(); player[i].put(30+i*70,210); } var result = Poker.checkHand(player); var rank = Poker.codeToRank(result); ++handList[rank]; var p = handList[rank] * 1.0 / allCount; p = Math.round(p * 10000); p = p / 10000; document.getElementById("hand").innerHTML = nameList[rank]; document.getElementById("hand"+rank).innerHTML = handList[rank].toString(); document.getElementById("prob"+rank).innerHTML = p.toString(); document.getElementById("alls").innerHTML = allCount.toString(); switch(rank){ //case 7: waitCount = 30; break; case 8: waitCount = 120; break; case 9: waitCount = 180; break; } return waitCount > 0; } function play(evt) { for(var i = 0; i < 5; ++i){ player[i].hidden(); trump.returnTop(player[i]); } trump.shuffle(); return show(); } function setList() { var ret = new Array(); ret.push('<table border = 0>'); ret.push('<tr>'); for(var j = 1; j >= 0; --j){ ret.push('<td>'); ret.push('<table border = 1 width="180">'); for(var i = ((j+1)*5)-1; i >= (j*5); --i){ ret.push('\ <tr>\ <td><font size="1">'+nameList[i]+'</font></td>\ <td><font size="1"><div id="hand'+i+'">'+handList[i]+'</div></font></td>\ <td><font size="1"><div id="prob'+i+'">0</div></font></td>\ </tr>'); } ret.push('</table>'); ret.push('</td>'); } ret.push('</tr></table>'); return ret.join(""); }