パーソナルブログメモリ

a = [1, 1]
for _ in "*" * 999: a += [sum(a[-2:])]
print(a)

Pygameを使って8時間ぐらいで一手で詰む画面を作ってみた

2022-09-30 | 新規1000万人ぐらいにプログラムをマスターしてもらいたい

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()


最新の画像もっと見る

コメントを投稿

ブログ作成者から承認されるまでコメントは反映されません。