Pygame 1.9.6利用
Pygameを使って3時間で一手詰め問題を作ってみようとする
の続き、駒の移動に関してデータをほぼ作り直し
追加時間 データ設計1時間 作成2時間半 デバッグ1時間半ぐらい
持ち駒なし、駒なりなし、駒取り勝ちなし、詰画面複数という妥協をしています
完成物
駒の表示系
盤の表示系
詰データ作成
駒数二歩確認
王手判定
王手逃げ判定
一手前作成
駒の効き(単連)
メインルーチン
データ設計
import time
import pygame
import random
from pygame.locals import *
class Unit: # 駒を表示させるためのクラス
def __init__(self,x,y,n,t):
self.x=x
self.y=y
self.n=n
self.t=t
def put(self):
x,y=self.x,self.y
text = font.render(self.n, True, (0,0,0))
screen.blit(text, [x*24+24, y*24+12])
if self.t: # 表示を反転
turn_screen(x*24+24,y*24+19,23,23)
def turn_screen(x,y,w,h): # screenの指定範囲を180度回転
c=[]
for y1 in range(y,y+h):
for x1 in range(x,x+w):
c+=[screen.get_at((x1, y1))]
for y1 in range(y,y+h):
for x1 in range(x,x+w):
screen.set_at((x1, y1), c.pop())
def test_bd(ps,c): # テスト用 駒の動ける範囲確認
bd="#"*22+"# #"*9+"#"*22
for p in ps:
bd=bd[:p]+c+bd[p+1:]
for y in range(2,12):
print(bd[y*w:y*w+w])
def print_bd(bd): # 作成しているテキスト盤の表示
for y in range(2,12):
print(bd[y*w:y*w+w])
def write_board(bd): # テキスト盤を画面変換表示する
for y in range(9):
for x in range(9):
pygame.draw.rect(screen, (255,200,0), Rect(x*24+24,y*24+19,23,23))
units=[]
for p,c in enumerate(bd):
cn="abcdefghijklmnABCDEFGHIJKLMN".find(c) # 下のknと対応(小文字は相手駒)
kn="歩香桂銀角飛金王と杏圭全馬竜歩香桂銀角飛金王と杏圭全馬竜"
if cn>=0:
x=p%11-1
y=p//11-2
if cn<=13: # 13以下は自分の駒
units+=[Unit(x,y,kn[cn],True)]
else:
units+=[Unit(x,y,kn[cn],False)]
for u in units:
u.put()
def put_bd(bd,x,y,c): # テキスト盤に駒を置く
p=x+1+y*11+22
return bd[:p]+c+bd[p+1:]
def board_add(bd,mr=0): # 詰むまで駒を置く
if mr==100: # 再帰の上限を100として
return space_bd # 越えたら枠だけを返す
if bd.find("h")==-1: # 相手王を最初に設定
x=random.randint(0,8)
y=random.randint(0,8)
return board_add(put_bd(bd,x,y,"h"),mr+1)
else: # 駒をランダムに配置
x=random.randint(0,8)
y=random.randint(0,8)
c="abcdefgijklmnABCDEFGIJKLMN"[random.randint(0,25)]
p=x+1+y*11+22
pas=False
if c in "ABC" and y==0:pas=True # 歩香桂の禁止位置判定
if c in "abc" and y==8:pas=True
if c=="C" and y==1:pas=True # 桂馬の禁止位置判定
if c=="c" and y==7:pas=True
if bd[p]==" " and not pas:
nbd=put_bd(bd,x,y,c)
pas=koma_su_ng(nbd) # 駒数判定
if pas: # エラーがなければ
if oute(nbd) and not away(nbd):return nbd # 詰 作成盤を返す
elif oute(nbd):return board_add(bd,mr+1) # 王手のみ追加せず再帰
else:return board_add(nbd,mr+1) # 駒を追加して再帰
return board_add(bd,mr+1) # エラーがあれば駒を追加せず再帰
def counts(bd,cs): # 指定文字(複数)がいくつあるか計算
return sum(bd.count(c) for c in cs)
def koma_su_ng(bd): # 駒の数が多くないかと二歩がないか
if counts(bd,"fnFN")>2:return False
if counts(bd,"emEM")>2:return False
if counts(bd,"bjBJ")>4:return False
if counts(bd,"ckCK")>4:return False
if counts(bd,"dlDL")>4:return False
if counts(bd,"gG")>4:return False
for x in range(9): # 二歩判定
line=""
for y in range(9):
line+=bd[x+1+y*11+22]
if line.count("a")>1:return False
if line.count("A")>1:return False
return True
def oute(bd): # 王手がかかっているか
pk=bd.find("h")
for p,c in enumerate(bd):
if c in "ABCDEFGHIJKLMN":
if pk in move_k1(bd,p,c)+move_k2(bd,p,c):
## print(pk%w-1,pk//w-2,p%w-1,p//w-2,c)
## print(move_k1(bd,p,c),move_k2(bd,p,c))
return True
return False
def away(bd): # 王手を回避する手があるか
## print_bd(bd)
for p,c in enumerate(bd):
if c in "abcdefghijklmn": # 相手駒を動かす
## test_bd(move_k1(bd,p,c)+move_k2(bd,p,c),c)
for np in move_k1(bd,p,c)+move_k2(bd,p,c):
y=np//w-2
nc=c
if y==8 and c in "abc":nc=chr(ord(c)+8) # 自動成
if y==7 and c in "c":nc="C" # 自動成
nbd=bd[:np]+nc+bd[np+1:]
nbd=nbd[:p]+" "+nbd[p+1:]
if not oute(nbd):return True
king_p=bd.find("h") # 王様の周りに駒を打って回避できるか
for p in move_k1(bd,king_p,"h"):
if bd[p]!=" ":continue
nbd=bd[:p]+"a"+bd[p+1:]
if not oute(nbd):return True
return False
def before(bd): # 一手戻して王手でないものから1つ返す
bds=[]
for p,c in enumerate(bd):
if c in "ABCDEFGHIJKLMN":
for np in move_k1(bd,p,c.lower())+move_k2(bd,p,c.lower()):
if bd[np]==" ":
nbd=bd[:np]+c+bd[np+1:]
nbd=nbd[:p]+" "+nbd[p+1:]
if not oute(nbd):bds+=[nbd]
if bds==[]:return bd
random.shuffle(bds)
return bds[0]
def move_k1(bd,p,c): # 駒の動ける範囲を返す
r=[]
move_ok=" abcdefghijklmn"
if c.islower():move_ok=move_ok.upper()
for d in k1.get(c.upper(),[]):
if c.islower():d=-d
if bd[p+d] in move_ok:r+=[p+d]
return r
def move_k2(bd,p,c): # 駒の動ける範囲を返す(連続用)
r=[]
move_ok=" abcdefghijklmn"
if c.islower():move_ok=move_ok.upper()
for d in k2.get(c.upper(),[]):
if c.islower():d=-d
for i in range(1,9):
if bd[p+d*i] in move_ok:r+=[p+d*i]
if bd[p+d*i] != " ":break
return r
def gamemain(): # クリックごとに一手詰画面を表示
sw=1
while 1:
screen.fill((0,0,0))
if sw==1:
bd=board_add(space_bd)
bbd=before(bd)
if bd==bbd:bd=space_bd
if bd!=space_bd:
write_board(bbd)
print_bd(bd)
print_bd(bbd)
pygame.display.update()
sw=0
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == MOUSEBUTTONUP and event.button == 1:
sw=1
# 駒の移動用のデータ作成
w=11
ks=[-w-1,-w,-w+1,-1,1,w]
kp=[-w,w,-1,1]
kx=[-w-1,-w+1,w-1,w+1]
k1={"A":[-w],"C":[-21,-23],"D":[-w-1,-w,-w+1,w-1,w+1],\
"G":ks,"I":ks,"J":ks,"K":ks,"L":ks,\
"H":ks+[w-1,w+1],"M":kp,"N":kx}
k2={"B":[-w],"E":kx,"M":kx,"F":kp,"N":kp}
space_bd="#"*22+"# #"*9+"#"*22 # 枠だけのボード
screen = pygame.display.set_mode((400, 400)) # 画面サイズ指定
pygame.init()
font = pygame.font.SysFont("notosansmonocjkjp", 23)
gamemain()
pygame.quit()