ポーカー(pyxel版)
ポーカーをレトロゲームエンジンpyxelで作ってみました。
カードとボタンの土台部分はpyxelのリソースエディタで作成、日本語の文字はpyxelで配布されているビットマップフォントとpyxelのサンプルソースから機能を抜き出して表示しています。
流れは以下の通りです。
1.手札が表示される
2.残したいカードは「かえる」をクリックして「のこす」に変更
3.「くばる」をクリックしてカードを交換、同時に役も表示
4.続ける場合は「続ける」、終了する場合は「終了」をクリック
5.続けるをクリックすると1から繰り返し
-- 実行するとカードとボタンが表示されるので、残すカードを選択する --
-- カード選択後「くばる」をクリックしてカードを交換する --
-- カード交換が行われ、役と続けるor終了の選択が表示される --
-- 続けるをクリックすると新たなカードが配られる。終了をクリックで終了する --
※「bmpfont」のimportはブログ内の「import用ソース」を参照
※フォントファイル「umplus_j12r.bdf」はpyxelで配布されているものを使用
※ジョーカーは初期値20枚(!)で設定してあります。高い役を目指してください!
ソースをGoogleドキュメントに載せました。
-ソース------------------------------------------------------
# ポーカー
import pyxel as px
import bmpfont as bmf
import random as rnd
#カードデータを内部で扱いやすくするために3桁の数値で表現する
#100の位を0~3とし、それぞれ、S(スペード)、H(ハート)、D(ダイヤ)、C(クラブ)とする
#下2桁を0~12とし、0~12をA2345678910JQKとする
#例:スペードのQ:内部データ=11、ハートのA内部データ=100
#ジョーカーは内部データ=400
#カードの枚数は52枚+ジョーカーの枚数
class App:
def __init__(self):
#画面サイズ設定
px.init(160,100, title="ポーカー")
#リソースファイルロード
px.load("0101ポーカー.pyxres")
#マウスON
px.mouse(True)
#Joker枚数指定
self.jcnt=20
#役名
self.Yaku=["ブタ","ワンペアー","ツーペアー","スリーカード","ストレート"
,"フラッシュ","フルハウス","フォーカード","ストレートフラッシュ"
,"ロイヤルストレートフラッシュ","ファイブカード","オールジョーカー"]
#フォント指定
self.umplus12 = bmf.BDFRenderer("umplus_j12r.bdf")
#ボタン文字
self.holdtxt=["かえる","のこす"]
self.dealtxt=["くばる",""]
self.conttxt=["続ける","終 了"] #※「終」と「了」の間は全角スペース
#マーク色セット
self.markcol=[0,1,1,0]
#変数初期化
self.startinit()
#ゲームスタート
px.run(self.update, self.draw)
#役判定関数
def Roychk(self,Nwk):
#ロイヤルの数のみ
chk=[0,9,10,11,12]
for i in Nwk:
if i not in chk:
return None
return 0
def Strahanichk(self,Nwk):
#数の最大-最小が5未満
if Nwk[len(Nwk)-1]-Nwk[0]<5:
return 0
def Fivecard(self,Ncnt):
#数の種類が0 ※全てジョーカー
if Ncnt==0:
return 11
#数の種類が1
elif Ncnt==1:
return 10
def RoyStraflush(self,Mcnt,Ncnt,Nwk):
#マークの種類が1&数が全て違う値
if Mcnt==1 and Ncnt==len(Nwk):
#ロイヤル判定
if self.Roychk(Nwk)==0:
return 9
def Straflush(self,Mcnt,Ncnt,Nwk):
#マークの種類が1&数が全て違う値
if Mcnt==1 and Ncnt==len(Nwk):
#数がストレート範囲内
if self.Strahanichk(Nwk)==0:
return 8
def Fourcard(self,Ncnt,Nwk):
#数が2種類
if Ncnt==2:
#数の最初か最後の個数が1
if Nwk.count(Nwk[0])==1 or Nwk.count(Nwk[len(Nwk)-1])==1:
return 7
def Fullhouse(self,Ncnt):
#数が2種類 ※フォーカードの形は判定済み
if Ncnt==2:
return 6
def Flush(self,Mcnt):
#マークが1種類
if Mcnt==1:
return 5
def Stra(self,Ncnt,Nwk):
#数が全て違う値
if Ncnt==len(Nwk):
#数がストレート範囲内orロイヤル
if self.Strahanichk(Nwk)==0 or self.Roychk(Nwk)==0:
return 4
def Threecard(self,Ncnt,Nwk):
#数が3種類
if Ncnt==3:
#個数=1が2種類以上
cnt=0
for i in set(Nwk):
if Nwk.count(i)==1:
cnt+=1
if cnt>=2:
return 3
def Twop(self,Ncnt,Nwk):
#数が3種類
if Ncnt==3:
#個数=1が1種類
cnt=0
for i in set(Nwk):
if Nwk.count(i)==1:
cnt+=1
if cnt==1:
return 2
def Onep(self,Ncnt):
#数が4種類
if Ncnt==4:
return 1
def Yakuhantei(self,Mcnt,Ncnt,Nwk):
#役判定
r=None
r=self.Fivecard(Ncnt)
if r==None:
r=self.RoyStraflush(Mcnt,Ncnt,Nwk)
if r==None:
r=self.Straflush(Mcnt,Ncnt,Nwk)
if r==None:
r=self.Fourcard(Ncnt,Nwk)
if r==None:
r=self.Fullhouse(Ncnt)
if r==None:
r=self.Flush(Mcnt)
if r==None:
r=self.Stra(Ncnt,Nwk)
if r==None:
r=self.Threecard(Ncnt,Nwk)
if r==None:
r=self.Twop(Ncnt,Nwk)
if r==None:
r=self.Onep(Ncnt)
if r==None:
r=0
return r
def Yamafuda(self):
#山札作成
for j in range(4):
for i in range(13):
#カード追加
self.l.append((j)*100+i)
for j in range(self.jcnt):
#ジョーカー追加
self.l.append(400)
#カードシャッフル
rnd.shuffle(self.l)
def firstdeal(self):
#初期手札データセット
self.Te=[]
for i in range(5):
#山札を手札にセット
self.Te.append(self.l[i])
#山札カウンタ+1
self.yamacnt+=1
def techg(self):
#手札交換
for i in range(5):
if self.holdbtn[i]==0:
#山札を手札にセット
self.Te[i]=self.l[self.yamacnt]
#山札カウンタ+1
self.yamacnt+=1
def yakudisp(self):
Mwk=[]
Nwk=[]
for i in self.Te:
if i!=400:
Mwk.append(i//100)
Nwk.append(i%100)
Nwk.sort()
r=self.Yakuhantei(len(set(Mwk)),len(set(Nwk)),Nwk)
self.contbtn=1
return self.Yaku[r]
def startinit(self):
#ボタン初期化
self.holdbtn=[0,0,0,0,0]
self.dealbtn=0
#山札作成
self.yamacnt=0
self.l=[]
self.Yamafuda()
#最初の5枚を配る
self.firstdeal()
#マウス用変数初期化
self.mx=0
self.my=0
self.btnno=-1
#役表示名初期化
self.Yakumei=""
#終了ボタン表示フラグ
self.contbtn=0
def update(self):
#Qキー押下で終了
if px.btnp(px.KEY_Q):
px.quit()
if px.btnp(px.MOUSE_BUTTON_LEFT):
self.mx=px.mouse_x
self.my=px.mouse_y
#かえる、のこすボタン
if self.mx<32*5 and self.my>=48 and self.my<48+16:
self.btnno=self.mx//32
self.holdbtn[self.btnno]=1-self.holdbtn[self.btnno]
else:
self.btnno=-1
#くばるボタン
if self.dealbtn==0 and self.mx>=32*2 and self.mx<32*3 and self.my>=80 and self.my<80+16:
self.dealbtn=1-self.dealbtn
#手札交換
self.techg()
#役名取得
self.Yakumei=self.yakudisp()
#終了ボタン
if self.contbtn==1:
if self.my>=64 and self.my<64+16:
if self.mx>=48 and self.mx<32+48:
#続ける
self.startinit()
elif self.mx>=32+48 and self.mx<32*2+48:
px.quit()
def draw(self):
#画面クリア
px.cls(0)
#くばるボタン表示
px.blt(64,80, 0, 0,96+self.dealbtn*16, 32,16, 0)
self.umplus12.draw_text(64+1, 80+2, self.dealtxt[self.dealbtn], 7, 5,-3)
#カードボタン表示
if self.dealbtn==0:
for i in range(5):
px.blt(i*32,48, 0, 0,96, 32,16, 0)
self.umplus12.draw_text(i*32+1, 48+2, self.holdtxt[self.holdbtn[i]], 7, 5,-3)
#カード土台表示
for i in range(5):
px.blt(i*32,0, 0, 0,48, 32,48, 0)
#カード内容表示
if self.Te[i]==400:
#ジョーカー表示処理
px.blt(i*32,16, 0, 64,0, 32,16, 0)
else:
#マーク表示
px.blt(i*32,16, 0, (self.Te[i]//100)*16,0, 16,16, 15)
#数表示
px.blt(i*32+16,16, 0, self.markcol[self.Te[i]//100]*16,32, 16,16, 0)
px.blt(i*32+16,16, 0, (self.Te[i]%100)*16,16, 16,16, 15)
#役表示 ※役確定までは役名変数に空文字が設定されている ※" "は全角スペース
self.umplus12.draw_text(0, 50, (7-len(self.Yakumei)//2)*" "+self.Yakumei, 7, 5,-1)
if self.contbtn==1:
#続ける・終了表示
for i in range(2):
px.blt(i*32+48,64, 0, 0,96, 32,16, 0)
self.umplus12.draw_text(i*32+50, 64+2, self.conttxt[i], 7, 5,-3)
App()
-------------------------------------------------------
リソースファイル(ファイル名:0101ポーカー.pyxres)では
・トランプのマーク(スペード、ハート、ダイヤ、クラブ)
・文字部分が透過色の数字(A,2,3,4,5,6,7,8,9,10,J,Q,K)とジョーカー
・マークに応じた数字の色(数字を透過させて使用)
・カードの下地
・ボタン(文字無し)
を作成しました。
※枠が小さくて入り切らないのでスクロールさせて表示しています
一枚絵にするとこんな感じです。
少々見づらいと思いますが、全体のイメージは掴みやすいかも知れません。
役判定は前のポーカーのロジックをほぼ流用出来ました。ただクラスやオブジェクト指向の理解がイマイチ(それ以前にpythonを理解していない…)のために後になって色々変えたくなったんですが、まず動く形にしようという方針で進めました。
恐らく、カードやボタンはクラス化して作った方がきれいに仕上がったと思いますが、その辺りは反省点として今後に生かしていこうと思います。
しかし、pythonド素人がグラフィカルなポーカー作れるんですから、pyxelってすごいですね!
今後も機会があれば使用していこうと思います。
以上です。