Processing による ポーカーゲームについて 110429085 八神孝嗣
Processing によるポーカーゲームについて 1. はじめに Processing でポーカーをつくったきっかけとしては, 指導教員が持ってきた資料の中でトランプを使ったゲームが多かったので, その中でも簡単だと思ったポーカーを選んだ. 2. ポーカーの歴史 ポーカーはトランプを使うゲームである 主にアメリカでプレイされているゲームで, 心理戦を特徴とし, ギャンブルとして行われる事が多い. 日本では, 主にクローズドポーカーと呼ばれる, 手札を隠す方式でプレイされる. 3. ルール ここではルールについて簡単に述べる. スペード, ハート, ダイヤ, クローバーの4つのマークのことをスートといい, それぞれの数字 A, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K をランクという. その中から5 枚のカードを選び, 役をつくって勝負する. 役には強さが設定されており, 役の種類は弱い順に次の 10 種類である. ノーペア ( 役なし ) ワンペア ( 同じランクのカードが 2 枚ある ) ツーペア ( ワンペアを 2 組つくる ) スリーカード ( 同じランクのカードを 3 枚つくる ) ストレート (5 つのカードのランクが連続している ) フラッシュ ( スートが全て同じ ) フルハウス ( スリーカードとワンペアを違うランクで同時につくる ) フォーカード ( 同じランクのカードが 4 枚ある ) ストレートフラッシュ ( ストレートとフラッシュを同時につくる ) ロイヤルストレートフラッシュ (A,K,Q,J,10 でストレートフラッシュをつくる ) また, ランクとスートにも強さが設定されており, 役が同じの場合, ランクとスートで勝敗を決める. ランクは A が最も強く,2 が最も弱い. スートの強さはスペード, ハート, ダイヤ, クラブの順である. 1
4. ゲームの進め方 ここでは, 今回作成したポーカープログラムについて説明する. 4.1 オープニング プログラムを起動させると, 最初にタイトルの POKER が表示され, キーボードを押す と, now loading を数秒の間, 表示されたのち, 自動的に Step.0 に移行する. Step.0 カードを配る場所に枠と, 中央上段に山札が描かれ, 山札をクリックすると Step.1 へ移 行する. 2
Step.1 山札から左側にカードが配られ, 配られると Step.2 へ移行する. Step.2 左側にカードが配られてから, さらに山札をクリックすると Step.3 へ移行する. Step.3 山札から右側にカードが配られ, 配られると Step.4 へ移行する. 3
Step.4 左右のカードが裏向きに描かれる. 左側のカードをクリックすると, 左側のカードを表向きに描き,Step.5 へ移行する. Step.5 左側のカードは表向きに描かれる. 右側のカードが裏向きに描かれる. 右側のカードをクリックすると, 右側のカードを表向きに描き,Step.6 へ移行する. Step.6 左右のカードが表向きに描かれる. ここで, キーボードを押すと Step.7 へ移行する. Step.7 4
左右の役の勝敗を決め, 役の名前と WIN,LOSE が描かれる. クリックすると Step.0 へ移行する. 5. プログラムの説明 Processing はオブジェクト指向に対応しているプログラム開発環境である. まず, 作成に利用するために用意したクラスについて述べる. 5.1 クラスの説明 Poker6 メインプログラムクラスや配列, 変数を宣言, 初期化し, ゲームの進行をしている変数 zに従って Step クラスに指示を出してゲームを進めている. 配列は全て 10 個ずつ宣言しているが, 配列の最初の5 個が左のカードに使うもので後の5 個が右のカードに使うものである. 変数の宣言で全てのカードの座標は最初に描く山札の位置に初期化している. Opening オープニングを表示するクラスオープニングを進める変数 wに従ってオープニングを進めている. 変数 c1,c2,c3 に関数 random を使うことでタイトルの色を表示するたびに変化させ, 変数 c4 を大きくしていくことで, タイトルの文字の濃度のだんだん濃くする. そして, キーボードの任意のキーを押すと次の段階に進む. 変数 nの値によって, now loading の後の.. の位置を決めて描画している. Judge 勝敗などを判定するクラス 5
関数 Duplicate はカード同士のかぶりをなくすための関数で if,for,while を使っている. まず, カードに割り当てられた数字が初期化された値 0だった場合, 関数 random を使い数字をそれぞれランダムに与えていく. そのあと if を使い, もしランダムに与えた数字に同じものがあれば, もう一度ランダムに与えていく. これを 10 枚のカードが全て違う数字になるまで続けていく. 関数 Suit は関数 Duplicate で与えられた数字をもとにランクとスートを決めていく関数で, 数字が2~14 ならばクラブの2~A となり,15~27 ならばハートの2~A となり,28~40 ならばダイヤの2~A となり,41~53 ならばスペードの2~A となる. ここでその値を配列 ranks と suits に格納し, 以降はこの2 つの配列を使っていく. 関数 hand は配列 ranks と suits を使いポーカーの役を判定する関数であり, ワンペア, ツーペア, スリーカード, フルハウス, フォーカードの判定は手札の 5 枚の中で同じランクのカードが何枚あるかで決めている. ストレートは5 個のランクを小さい順に並び変え,5 個とも連番になっているかで決めている. フラッシュは5 枚とも同じスートか判定している. ストレートフラッシュはストレートかつフラッシュのときになる. ロイヤルストレートフラッシュはストレートフラッシュの中でも最小のランクが 10 のときになる. 関数 bout は関数 hand で決めた役をもとに左右の役がどちらが強いか判定する関数で, 同じ役同士ならば大きいランクのものが勝つようになっている. Step ゲームを進めるクラス前述のとおりにゲームを進めていく. Trump 描画するクラス関数 Frame は与えられたx 座標 y 座標によって枠を描画する関数である. 関数 Bottom は与えられたx 座標 y 座標によってカードを裏向きにを描画する関数である. 関数 Surface は与えられたx 座標 y 座標によってカードを表向きにを描画する関数であり, 与えられる ranks と suits の値によって描画する. 関数 Hname は関数 hand で決まった役の名前を描画する関数である. 関数 Win と関数 Lose は勝負に勝った方に Win を負けた方に Lose を描画する関数である. 6. ソースプログラム 最後に全ソースプログラムを記載する. 6.1 Poker6 6
Opening opening; // オープニングのクラス Trump trump; // トランプなどを描画するクラス Step step; // ゲームを進めるクラス Judge judge; // 判定するクラス int[] numbers=new int[10]; // 各カードの数字の配列 int[] ranks=new int[10]; // 各カードのランクの配列 int[] suits=new int[10]; // 各カードのスートの配列 float[] cardxs=new float[10]; // 各カードの x 座標の配列 int LY=100; // 左のカードの y 座標 int RY=100; // 右のカードの y 座標 int LH=0; // 左のカードのハンドの強さ int LHr=0; // 左のカードのハンドのランクの強さ int LHs=0; // 左のカードのハンドスート int RH=0; int RHr=0; int RHs=0; // 右のカードのハンドの強さ // 右のカードのハンドのランクの強さ // 右のカードのハンドスート int z=0; // ゲームを進行するための変数 int n=0; // now loading を描画するための変数 PImage clubs; // クラブの画像を使う宣言 PImage diamonds; // ダイヤの画像を使う宣言 PImage hearts; // ハートの画像を使う宣言 PImage spades; // スペードの画像を使う宣言 void setup() { size(1230,500); smooth(); background(255); rectmode(center); strokeweight(5); textalign(center); 7
opening=new Opening(); trump=new Trump(); step=new Step(); judge=new Judge(); for(int i=0;i<numbers.length;i++) { numbers[i]=0; for(int i=0;i<ranks.length;i++) { ranks[i]=0; for(int i=0;i<suits.length;i++) { suits[i]=0; for(int i=0;i<cardxs.length;i++) { cardxs[i]=615; clubs=loadimage("clubs.png"); diamonds=loadimage("diamonds.png"); hearts=loadimage("hearts.png"); spades=loadimage("spades.png"); imagemode(center); void draw() { background(255); trump.bottom(width/2,100); judge.duplicate(); judge.suit(); if(z=-1) { opening.ope(); 8
if(z==0) { step.s0(); else if(z==1) { step.s1(); else if(z==2) { step.s2(); else if(z==3) { step.s3(); else if(z==4) { step.s4(); else if(z==5) { step.s5(); else if(z==6) { step.s6(); else if(z==7) { step.s7(); 6.2 Opening class Opening { int w=0; // オープニングを進めるための変数 float c1=random(255); // タイトルの色を決める変数 float c2=random(255); // タイトルの色を決める変数 float c3=random(255); // タイトルの色を決める変数 int c4=0; // タイトルの色の濃さを決める変数 9
void Ope() { // オープニングを進める関数 if(w==0) { opening.title(); else if(w==1) { opening.loading(); void Title() { // タイトル POKER を表示する関数 fill(c1,c2,c3,c); textsize(390); text("poker",width/2,height-100); textsize(30); fill(0,c); text("press any key",width/2,height/2+120); c++; if(keypressed) { w=1; void Loading() { // now loading を表示する関数 fill(0); textsize(30); if(n%20>=0&&n%20<=4) { text("now loading ",width/2,height/2); if(n%20>=5&&n%20<=9) { text("now loading. ",width/2,height/2); if(n%20>=10&&n%20<=14) { text("now loading.. ",width/2,height/2); if(n%20>=15&&n%20<=19) { 10
text("now loading...",width/2,height/2); n++; if(n>90) { z=0; 6.3 Judge class Judge { void Duplicate() { // カード同士のかぶりをなくす関数 if(numbers[0]==0) { for(int i=0;i<numbers.length;i++) { numbers[i]=int(random(2,53)); for(int i=0;i<numbers.length;i++) { for(int j=i+1;j<numbers.length;j++) { while(numbers[i]==numbers[j]) { numbers[j]=int(random(2,53)); void Suit() { // カードのスートを決める関数 for(int i=0;i<ranks.length;i++) { if(numbers[i]>=2&&numbers[i]<=14) { ranks[i]=numbers[i]; suits[i]=1; else if(numbers[i]>=15&&numbers[i]<=27) { ranks[i]=numbers[i]-13; 11
suits[i]=2; else if(numbers[i]>=28&&numbers[i]<=40) { ranks[i]=numbers[i]-26; suits[i]=3; else if(numbers[i]>=41&&numbers[i]<=53) { ranks[i]=numbers[i]-39; suits[i]=4; void hand(int b) { // ハンドの種類を決める関数 int h=0; int hr=0; int hs=0; int p=0; int q=0; int r=0; int s=0; int str[]=new int[10]; for(int i=0;i<str.length;i++) { str[i]=ranks[i]; for(int i=b;i<b+5;i++) { // ワンペア, ツーペア, スリーカード, for(int j=i+1;j<b+5;j++) { フルハウス, フォーカードの判定 if(ranks[i]==ranks[j]) { p++; if(p==1) { h=1; hr=ranks[i]; hs=suits[i]; 12
else if(p==2) { h=2; if(hr<ranks[i]) { hr=ranks[i]; hs=suits[i]; else if(p==3) { h=3; if(hr<ranks[i]) { hr=ranks[i]; hs=suits[i]; else if(p==4) { h=6; if(hr<ranks[i]) { hr=ranks[i]; hs=suits[i]; else if(p==6) { h=7; if(hr<ranks[i]) { hr=ranks[i]; hs=suits[i]; for(int i=b;i<b+5;i++) { // ストレートの判定 for(int j=i+1;j<b+5;j++) { if(str[i]<str[j]) { r=str[j]; 13
str[j]=str[i]; str[i]=r; if(str[b]-str[b+1]==1&&str[b+1]-str[b+2]==1&&str[b+2]-str[b+3]==1&&str[b +3]-str[b+4]==1 str[b]==14&&str[b+1]-str[b+2]==1&&str[b+2]-str[b+3]==1& &str[b+3]-str[b+4]==1&&str[b+4]==2) { h=4; hs=suits[b]; hr=str[b]; s=1; for(int i=b+1;i<b+5;i++) { // フラッシュの判定 if(suits[b]==suits[i]) { q++; if(q==4&&p<q) { h=5; hs=suits[i]; if(h==5&&s==1) { // ストレートフラッシュの判定 h=8; if(str[b+4]==10) { // ロイヤルストレートフラッシュの判定 h=9; if(h==0) { // ノーペアの判定 for(int i=b;i<b+5;i++) { if(hr<ranks[i]) { hr=ranks[i]; 14
hs=suits[i]; if(b==0) { LH=h; LHr=hr; LHs=hs; else if(b==5) { RH=h; RHr=hr; RHs=hs; void bout() { // ハンド同士を勝負させる関数 trump.hname(lh,295); trump.hname(rh,935); if(lh>rh) { trump.win(295); trump.lose(935); else if(lh<rh) { trump.lose(295); trump.win(935); else if(lh==rh) { if(lh==5) { if(lhs>rhs) { trump.win(295); trump.lose(935); else if(lhs<rhs) { 15
trump.lose(295); trump.win(935); else { if(lhr>rhr) { trump.win(295); trump.lose(935); else if(lhr<rhr) { trump.lose(295); trump.win(935); else if(lhr==rhr) { if(lhs>rhs) { trump.win(295); trump.lose(935); else if(lhs<rhs) { trump.lose(295); trump.win(935); 6.4 Step class Step { void s0() { for(int i=0;i<5;i++) { trump.frame(75+i*110,400); trump.frame(1155-i*110,400); 16
if(mousex>=615-50&&mousex<=615+50&&mousey>=25&&mousey<=175&&mousepressed ) { z=1; void s1() { for(int i=0;i<5;i++) { trump.frame(75+i*110,400); trump.frame(1155-i*110,400); for(int i=0;i<2;i++) { for(int j=0;j<5;j++) { trump.bottom(cardxs[j],ly); cardxs[0]-=7.2; cardxs[1]-=5.7333; cardxs[2]-=4.26666; cardxs[3]-=2.8; cardxs[4]-=1.3333; LY+=4; if(ly>=400) { z=2; void s2() { for(int i=0;i<5;i++) { trump.frame(1155-i*110,400); 17
for(int i=0;i<5;i++) { trump.bottom(cardxs[i],ly); if(mousex>=615-50&&mousex<=615+50&&mousey>=25&&mousey<=175&&mousepressed ) { z=3; void s3() { for(int i=0;i<5;i++) { trump.frame(1155-i*110,400); for(int i=0;i<5;i++) { trump.bottom(cardxs[i],ly); for(int i=0;i<2;i++) { for(int j=5;j<10;j++) { trump.bottom(cardxs[j],ry); cardxs[5]+=7.2; cardxs[6]+=5.7333; cardxs[7]+=4.26666; cardxs[8]+=2.8; cardxs[9]+=1.33333; RY+=4; if(ry>=400) { z=4; 18
void s4() { for(int i=0;i<ranks.length;i++) { trump.bottom(cardxs[i],ly); if(mousex>=25&&mousex<=565&&mousey>=325&&mousey<=475&&mousepressed) { for(int i=0;i<5;i++) { trump.surface(cardxs[i],ly,ranks[i],suits[i]); z=5; void s5() { for(int i=0;i<5;i++) { trump.surface(cardxs[i],ly,ranks[i],suits[i]); for(int i=5;i<ranks.length;i++) { trump.bottom(cardxs[i],ry); if(mousex>=665&&mousex<=1205&&mousey>=325&&mousey<=475&&mousepressed) { for(int i=5;i<ranks.length;i++) { trump.surface(cardxs[i],ry,ranks[i],suits[i]); z=6; void s6() { for(int i=0;i<ranks.length;i++) { trump.surface(cardxs[i],ly,ranks[i],suits[i]); 19
if(keypressed) { z=7; void s7() { for(int i=0;i<ranks.length;i++) { trump.surface(cardxs[i],ly,ranks[i],suits[i]); judge.hand(0); judge.hand(5); judge.bout(); if(mousex>=615-50&&mousex<=615+50&&mousey>=25&&mousey<=175&&mousepressed ) { for(int i=0;i<cardxs.length;i++) { cardxs[i]=615; LY=100; RY=100; numbers[0]=0; z=0; n=0; 6.5 Trump class Trump { 20
void Frame(int x,int y) { // カードを配る枠を描く関数 stroke(150); fill(255); rect(x,y,100,150,10); void Bottom(float x,int y) { // カードの裏側を描く関数 stroke(0); fill(255); rect(x,y,100,150,10); fill(0,0,255); rect(x,y,80,130,10); void Surface(float x,int y,int r,int s) { // カードの表側を描く関数 stroke(0); fill(255); rect(x,y,100,150,10); if(s==1) { fill(0); image(clubs,x,y+30,50,50); else if(s==2) { fill(255,0,0); image(diamonds,x,y+30,50,50); else if(s==3) { fill(255,0,0); image(hearts,x,y+30,50,50); else if(s==4) { fill(0); image(spades,x,y+30,50,50); 21
textsize(50); if(r==14) { text("a",x,y-20); else if(r==11) { text("j",x,y-20); else if(r==12) { text("q",x,y-20); else if(r==13) { text("k",x,y-20); else { text(r,x,y-20); void Hname(int h,int x) { // ハンドの名前を描く関数 fill(0); if(h==0) { text("no Pair",x,height/2+50); else if(h==1) { text("one Pair",x,height/2+50); else if(h==2) { text("two Pair",x,height/2+50); else if(h==3) { text("three of a Kind",x,height/2+50); else if(h==4) { text("straight",x,height/2+50); else if(h==5) { 22
text("flash",x,height/2+50); else if(h==6) { text("full House",x,height/2+50); else if(h==7) { text("four of a Kind",x,height/2+50); else if(h==8) { text("straight Flush",x,height/2+50); else if(h==9) { text("royal Straight Flush",x,height/2+50); void Win(float x) { //WIN を描く関数 fill(255,0,0); text("win",x,height/2-20); void Lose(float x) { //LOSE を描く関数 fill(0,0,255); text("lose",x,height/2-20); 7. まとめと今後の課題 役の判定が難しく, 特にストレートの判定は難問であった. ストレートの種類は A,2,3,4,5 と 10,J,Q,K,A の A を使うストレートだけでも2 種類あるので, その誤差を直すのと,5 個の数字が連番になっているかを調べることが大変であった. 今後の課題としては, ポーカーは手札を好きな枚数だけ交換することができるので, 交換までできればと考えている. 23