2019年3月24日日曜日

Pythonでゲーム作成(Hextrisゲーム)

「Pythonでテトリス」を書き換えてHextrisを作ってみました.

画面はこんな感じです.


昔,X68000で遊んでいたゲームです.最近は同じ名前の別のゲームがあるようですが...

以下がソースコードです.


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Python 2.7.5 on OS X 10.9.2
# by Mitsuharu Arimura 2015/3/14

import Tkinter as Tk
import datetime as dt
import time, random, math
import sys, os, csv

# ゲームフィールドのサイズ
FIELD_WIDTH = 17
FIELD_HEIGHT = 24

# ブロックのサイズ
PIECE_WIDTH = 4
PIECE_HEIGHT = 4

# 1個の六角形セルの1辺の長さ
CELL_SIZE = 15

SQRT3 = math.sqrt(3.0)

# 六角形を作る関数
def CreateHexagon( canvas, x, y, offset ):
    # (x0, y0): 六角形の中心
    x0 = CELL_SIZE * (x * 1.5 + 1) + offset
    y0 = CELL_SIZE * (y + 0.5 + x * 0.5) * SQRT3 + offset
    #print "(%d, %d): (%.1f, %.1f)" % (x, y, x0, y0)
    # vtx_x, vty_y: 六角形の頂点のx座標と座標の配列(6個)
    vtx_x = [ x0 + CELL_SIZE * math.cos(i*math.pi/3.0)
              for i in range(0, 6) ]
    vtx_y = [ y0 + CELL_SIZE * math.sin(i*math.pi/3.0)
              for i in range(0, 6) ]
    #print "x: ", vtx_x
    #print "y: ", vtx_y
    r = canvas.create_polygon( vtx_x[0], vtx_y[0],
                             vtx_x[1], vtx_y[1],
                             vtx_x[2], vtx_y[2],
                             vtx_x[3], vtx_y[3],
                             vtx_x[4], vtx_y[4],
                             vtx_x[5], vtx_y[5],
                             fill='white',
                             outline='gray',
                             width=1)
    return r

# ゲーム本体の画面 View
class Board( Tk.Canvas ):
    # フィールドのピクセル数
    BOARD_WIDTH = (FIELD_WIDTH + 0.5) * CELL_SIZE * 1.5
    BOARD_HEIGHT = (FIELD_HEIGHT + 0.5) * CELL_SIZE * SQRT3 + 3
    margin = 2

    def __init__( self, master ):
        self.width = self.BOARD_WIDTH
        self.height = self.BOARD_HEIGHT
        Tk.Canvas.__init__( self, master, relief=Tk.RAISED, bd=2, bg='gray',
                            width=self.BOARD_WIDTH, height=self.BOARD_HEIGHT )
        #print "Canvas width %d x %d" % ( self.BOARD_WIDTH, self.BOARD_HEIGHT )

        # Canvas上の rectangle の2次元配列
        # 周囲に1マスずつ余計に取って,グレーに塗り潰しておくのが簡単
        self.Field = [[0 for y in range(- int(x*0.5), FIELD_HEIGHT-int(x*0.5))]
                      for x in range(FIELD_WIDTH)]
        offset = self.margin * 2 + 3
        for x in range( FIELD_WIDTH ):
            for y in range( -int(x*0.5), FIELD_HEIGHT-int(x*0.5) ):
                self.Field[x][y] = CreateHexagon( self, x, y, offset )

        # PAUSEの文字
        TEXT_font = ('Helvetica','60','bold')
        TEXT_color = 'red'
        self.pause_text = self.create_text( self.BOARD_WIDTH/2, 100,
                                            text='PAUSE',
                                            font=TEXT_font,
                                            fill=TEXT_color,
                                            justify=Tk.CENTER)
        self.hidePauseText()

    def SetColor( self, x, y, value ):
        #print "SetColor:"
        if x < 0: return
        if x >= FIELD_WIDTH: return
        if y < -int(x * 0.5): return
        if y >= FIELD_HEIGHT - int(x * 0.5): return
        #print "(%d, %d)=>%d" % (x, y, value)
        if value == 1:
            self.itemconfigure( self.Field[x][y], fill='black')
        elif value == 2:
            self.itemconfigure( self.Field[x][y], fill='gray')
        elif value == 3:
            self.itemconfigure( self.Field[x][y], fill='slate gray')
        else:
            self.itemconfigure( self.Field[x][y], fill='white')

    # PAUSE中にPAUSEの文字を表示する
    def showPauseText( self ):
        self.itemconfigure( self.pause_text, state=Tk.NORMAL )

    # PAUSEが終わるときにPAUSEの文字を非表示にする
    def hidePauseText( self ):
        self.itemconfigure( self.pause_text, state=Tk.HIDDEN )

# 次のピースを表示する画面 View
class NextPieceBoard( Tk.Canvas ):
    # 次のピースを表示する場所のサイズ
    NEXT_BOARD_WIDTH = (PIECE_WIDTH + 0.5) * CELL_SIZE * 1.5
    NEXT_BOARD_HEIGHT = (PIECE_HEIGHT + 1.5) * CELL_SIZE * SQRT3 + 3
    margin = 2
    def __init__( self, master ):
        Tk.Canvas.__init__( self, master, relief=Tk.RAISED, bd=self.margin,
                            bg='white',
                            width=self.NEXT_BOARD_WIDTH,
                            height=self.NEXT_BOARD_HEIGHT )
        self.RectArray = [[0 for y in range(PIECE_HEIGHT)]
                          for x in range(PIECE_WIDTH)]
        #print self.RectArray
        offset = self.margin * 2 + 3
        for x in range( PIECE_WIDTH ):
            for y in range( PIECE_HEIGHT ):
                self.RectArray[x][y] = CreateHexagon( self, x, y, offset )

    def DisplayPiecePattern( self, pattern ):
        p = pattern
        for x in range( PIECE_WIDTH ):
            for y in range( PIECE_HEIGHT ):
                #print "p[%d][%d] = %d" % (x, y, p[x][y])
                if p[x][y] == 1:
                    # print "p[%d][%d] = red" % (x, y)
                    self.itemconfigure( self.RectArray[x][y], fill='black' )
                else:
                    # print "p[%d][%d] = blue" % (x, y)
                    self.itemconfigure( self.RectArray[x][y], fill='white' )

# ゲーム全体の画面および Controller
class Frame( Tk.Frame ):
    def __init__( self, master=None ):
        Tk.Frame.__init__( self, master, width=600, height=800 )
        self.master.title( 'Hextris' )

        #########################################
        # board frame
        #BOARD_WIDTH = (FIELD_WIDTH + 0.5) * CELL_SIZE * 1.5
        #BOARD_HEIGHT = (FIELD_HEIGHT + 0.5) * CELL_SIZE * SQRT3 + 3
        self.bframe = Tk.Frame( self, bd=1, relief=Tk.RIDGE )
        self.bframe.pack( side=Tk.LEFT, padx=10, pady=10 )
        self.board = Board( self.bframe )
        self.board.pack( side=Tk.LEFT, padx=0, pady=0 )

        #########################################
        # display frame
        frame = Tk.Frame( self, bd=1, relief=Tk.RIDGE )
        # exit button
        self.btExit = Tk.Button( frame, text='Exit', command=self.exitGame )
        self.btExit.pack( anchor=Tk.E, padx=10, pady=10 )
        # replay (not start) button
        #self.btReplay = Tk.Button( frame, text='Replay',
        #                           command=self.replayGame )
        #self.btReplay.pack( anchor=Tk.E, padx=10, pady=0 )
        # start button
        self.btStart = Tk.Button( frame, text='Start', command=self.startGame )
        self.btStart.pack( anchor=Tk.E, padx=10, pady=0 )
        # next piece display
        self.next_piece = NextPieceBoard( frame )
        self.next_piece.pack( anchor = Tk.E, padx=10, pady=10 )
        TEXT_font = ('Helvetica','12','italic')
        NUM_font = ('Helvetica','24','bold')
        # time text
        self.time_text = Tk.Label( frame, text='PLAY TIME', font=TEXT_font )
        self.time_text.pack( anchor = Tk.E, padx=10, pady=0 )
        # timer
        self.time_box = Tk.Label( frame, text='00\'00\"', font=NUM_font,
                                  bd=1, relief=Tk.RIDGE )
        self.time_box.pack( anchor = Tk.E, padx=10, pady=0 )
        frame.pack( side=Tk.RIGHT, padx=10, pady=10 )

        # key bind
        self.bind( "", self.keyPressed )
        self.bind( "", self.keyLeft )
        self.bind( "", self.keyRight )
        self.bind( "", self.keyUp )
        self.bind( "", self.keyDown )
        self.focus_set()

        self.hextris = Hextris( self )

        self.processing_down = False
        self.timerEventCount = 0
        self.timerCount = 0

        #self.DisableReplayButton()
        self.hextris.PrepareStartGame()

    # exit button
    def exitGame( self ):
        self.hextris.playing = False
        sys.exit()

    # start button
    def startGame( self ):
        self.hextris.playing = True
        # ボタンは押せなくする
        self.DisableStartButton()
        #self.DisableReplayButton()
        # カウンタをリセットしてタイマーをスタートする
        self.timerEventCount = 0
        self.timerCount = 0
        # これ以降,timerEventが0.5秒に一回呼び出される
        self.after( 500, self.timerEvent )

    # prepare replay
    def replayGame( self ):
        self.EnableStartButton()
        self.hextris.PrepareStartGame()
        self.timerEventCount = 0
        self.timerCount = 0
        self.DisplayTime( 0, 0 )

    # 0.5秒ごとに呼び出される
    def timerEvent( self ):
        if self.hextris.playing == False:
            return
        if self.hextris.pausing == True:
            return
        self.timerEventCount = self.timerEventCount + 1
        # 0.5秒に1回呼び出されるので2回→1秒に1回の点数アップ
        if self.timerEventCount % 2 == 0:
            self.timerCount = self.timerCount + 1
            self.DisplayTime( self.timerCount / 60, self.timerCount % 60 )
            # print "count %d" % self.timerEventCount
            # print "timer %d" % self.timerCount
        # ブロックを1段落とす
        self.keyDown( None )
        # 0.5秒後に自分を呼び出す
        self.after( 500, self.timerEvent )

    def endGame( self ):
        self.focus_set()
        self.replayGame()

    def DisableExitButton( self ):
        self.btExit.configure( state=Tk.DISABLED )

    def EnableExitButton( self ):
        self.btExit.configure( state=Tk.NORMAL )

    def DisableReplayButton( self ):
        #self.btReplay.configure( state=Tk.DISABLED )
        pass

    def EnableReplayButton( self ):
        #self.btReplay.configure( state=Tk.NORMAL )
        pass

    def DisableStartButton( self ):
        self.btStart.configure( state=Tk.DISABLED )

    def EnableStartButton( self ):
        self.btStart.configure( state=Tk.NORMAL )

    def DisplayTime( self, min=0, sec=0 ):
        self.time_box.configure( text="%02d\'%02d\"" % (min, sec) )

    # 以下はキー入力処理
    def keyPressed( self, event ):
        c = event.char
        # space: pause
        if self.hextris.playing == False:
            return
        if c == ' ':
            if self.hextris.pausing == False:
                print "PAUSE!!!"
                self.hextris.pausing = True
                # PAUSEを表示
                self.board.showPauseText()
            else:
                print "PAUSE END!!!"
                self.hextris.pausing = False
                # PAUSEを隠す
                self.board.hidePauseText()
                # 再度タイマーを開始
                self.after( 500, self.timerEvent )

    def keyLeft( self, event ):
        if self.hextris.playing == False:
            return
        if self.hextris.pausing == True:
            return
        # print "press Left"
        if self.hextris.CanMovePiece( move='left' ) == 0:
            self.hextris.MovePiece( move='left' )
            self.hextris.RedrawAllField()

    def keyRight( self, event ):
        if self.hextris.playing == False:
            return
        if self.hextris.pausing == True:
            return
        # print "press Right"
        if self.hextris.CanMovePiece( move='right' ) == 0:
            self.hextris.MovePiece( move='right' )
            self.hextris.RedrawAllField()

    # キーで呼ばれるのとタイマーで呼ばれるのが同時に重なる場合が
    # あるので再入防止している
    def keyDown( self, event ):
        #print "keyDown"
        if self.hextris.playing == False:
            return
        if self.hextris.pausing == True:
            return
        # print "press Down"
        if self.processing_down == True:
            return
        self.processing_down = True
        res = self.hextris.CanMovePiece( move='down' )
        if res == 0:
            self.hextris.MovePiece( move='down' )
            self.hextris.RedrawAllField()
        elif res == 2:
            self.hextris.FixPieceAndGetNextPiece()
        self.processing_down = False

    # 回転
    def keyUp( self, event ):
        if self.hextris.playing == False:
            return
        if self.hextris.pausing == True:
            return
        # print "press Up"
        if self.hextris.CanTurnPiece() == True:
            self.hextris.TurnPiece()
            self.hextris.RedrawAllField()

# Hextrisゲームの Data
class Hextris:
    # ゲームフィールド
    field = [[0 for y in range(FIELD_HEIGHT)]
             for x in range(FIELD_WIDTH)]
    # 現在移動中のブロック
    piece = [[0 for y in range(PIECE_HEIGHT)]
             for x in range(PIECE_WIDTH)]
    # ブロックの左上端の座標(フィールドは左上が(x,y) = (0,0)でyが下向き)
    location = [0, 0]

    # ブロック
    # 種類の数を揃えておくこと!!!
    # listにaddしていくと,ここで数を書く必要は無い
    ID_NUM = 10
    id = 0 # 0...ID_NUM-1の8種類
    id_next = 0 # 次のピース
    ORIENT_NUM = 6
    orientation = 0 # 0...ORIENT_NUM-1の6種類

    # 次のブロック
    nextPiece = [[[0 for y in range(PIECE_HEIGHT)]
                  for x in range(PIECE_WIDTH)]
                 for o in range(ORIENT_NUM)]

    # ブロックの全てのパターン index, orientation, x, y毎に1, 0
    piecePattern = [[[[0 for y in range(PIECE_HEIGHT)]
                      for x in range(PIECE_WIDTH)]
                     for o in range(ORIENT_NUM)]
                    for i in range(ID_NUM)]
    piecePattern[0] = [
        [[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]], # 方向0の[x][y]
        [[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0]], # 方向1
        [[0,0,0,1],[0,0,1,0],[0,1,0,0],[1,0,0,0]], # 方向2
        [[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]], # 方向3
        [[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0]], # 方向4
        [[0,0,0,1],[0,0,1,0],[0,1,0,0],[1,0,0,0]]] # 方向5
    piecePattern[1] = [
        [[0,0,0,0],[1,1,0,0],[0,1,1,0],[0,0,0,0]], # 方向0の[x][y]
        [[0,0,1,0],[0,0,1,0],[0,1,0,0],[0,1,0,0]], # 方向1
        [[0,0,0,1],[0,1,1,0],[1,0,0,0],[0,0,0,0]], # 方向2
        [[0,0,0,0],[1,1,0,0],[0,1,1,0],[0,0,0,0]], # 方向3
        [[0,0,1,0],[0,0,1,0],[0,1,0,0],[0,1,0,0]], # 方向4
        [[0,0,0,1],[0,1,1,0],[1,0,0,0],[0,0,0,0]]] # 方向5
    piecePattern[2] = [
        [[0,0,0,0],[0,0,1,1],[1,1,0,0],[0,0,0,0]], # 方向0の[x][y]
        [[0,1,0,0],[0,1,1,0],[0,0,1,0],[0,0,0,0]], # 方向1
        [[0,0,1,0],[0,1,0,0],[0,1,0,0],[1,0,0,0]], # 方向2
        [[0,0,0,0],[0,0,1,1],[1,1,0,0],[0,0,0,0]], # 方向3
        [[0,1,0,0],[0,1,1,0],[0,0,1,0],[0,0,0,0]], # 方向4
        [[0,0,1,0],[0,1,0,0],[0,1,0,0],[1,0,0,0]]] # 方向5
    piecePattern[3] = [
        [[0,1,0,0],[1,1,0,0],[1,0,0,0],[0,0,0,0]], # 方向0の[x][y]
        [[0,1,1,0],[1,1,0,0],[0,0,0,0],[0,0,0,0]], # 方向1
        [[0,1,1,0],[0,1,1,0],[0,0,0,0],[0,0,0,0]], # 方向2
        [[0,1,0,0],[1,1,0,0],[1,0,0,0],[0,0,0,0]], # 方向3
        [[0,1,1,0],[1,1,0,0],[0,0,0,0],[0,0,0,0]], # 方向4
        [[0,1,1,0],[0,1,1,0],[0,0,0,0],[0,0,0,0]]] # 方向5
    piecePattern[4] = [
        [[0,0,0,0],[0,0,0,1],[1,1,1,0],[0,0,0,0]], # 方向0の[x][y]
        [[0,1,0,0],[0,1,0,0],[0,1,1,0],[0,0,0,0]], # 方向1
        [[0,0,0,1],[0,0,1,0],[0,1,0,0],[0,1,0,0]], # 方向2
        [[0,0,0,0],[0,1,1,1],[1,0,0,0],[0,0,0,0]], # 方向3
        [[0,1,1,0],[0,0,1,0],[0,0,1,0],[0,0,0,0]], # 方向4
        [[0,0,1,0],[0,0,1,0],[0,1,0,0],[1,0,0,0]]] # 方向5
    piecePattern[5] = [
        [[0,0,0,0],[1,1,1,0],[0,0,1,0],[0,0,0,0]], # 方向0の[x][y]
        [[0,1,0,0],[0,1,0,0],[0,1,0,0],[1,0,0,0]], # 方向1
        [[0,0,0,1],[0,0,1,0],[1,1,0,0],[0,0,0,0]], # 方向2
        [[0,0,0,0],[1,0,0,0],[1,1,1,0],[0,0,0,0]], # 方向3
        [[0,1,0,0],[1,0,0,0],[1,0,0,0],[1,0,0,0]], # 方向4
        [[0,0,1,1],[0,1,0,0],[1,0,0,0],[0,0,0,0]]] # 方向5
    piecePattern[6] = [
        [[0,0,0,0],[0,0,1,0],[1,1,1,0],[0,0,0,0]], # 方向0の[x][y]
        [[0,1,0,0],[0,1,1,0],[0,1,0,0],[0,0,0,0]], # 方向1
        [[0,0,1,0],[0,1,0,0],[1,1,0,0],[0,0,0,0]], # 方向2
        [[0,0,0,0],[0,1,1,1],[0,1,0,0],[0,0,0,0]], # 方向3
        [[0,0,1,0],[0,1,1,0],[0,0,1,0],[0,0,0,0]], # 方向4
        [[0,0,0,0],[0,0,1,1],[0,0,1,0],[0,1,0,0]]] # 方向5
    piecePattern[7] = [
        [[0,0,0,0],[0,1,1,1],[0,0,1,0],[0,0,0,0]], # 方向0の[x][y]
        [[0,0,1,0],[0,0,1,0],[0,1,1,0],[0,0,0,0]], # 方向1
        [[0,0,0,0],[0,0,1,0],[1,1,0,0],[1,0,0,0]], # 方向2
        [[0,0,0,0],[0,1,0,0],[1,1,1,0],[0,0,0,0]], # 方向3
        [[0,1,1,0],[0,1,0,0],[0,1,0,0],[0,0,0,0]], # 方向4
        [[0,0,1,0],[0,1,1,0],[1,0,0,0],[0,0,0,0]]] # 方向5
    piecePattern[8] = [
        [[0,1,1,0],[1,0,0,0],[1,0,0,0],[0,0,0,0]], # 方向0の[x][y]
        [[0,1,1,0],[1,0,1,0],[0,0,0,0],[0,0,0,0]], # 方向1
        [[0,1,1,0],[0,0,1,0],[0,1,0,0],[0,0,0,0]], # 方向2
        [[0,0,1,0],[0,0,1,0],[1,1,0,0],[0,0,0,0]], # 方向3
        [[0,0,0,0],[1,0,1,0],[1,1,0,0],[0,0,0,0]], # 方向4
        [[0,1,0,0],[1,0,0,0],[1,1,0,0],[0,0,0,0]]] # 方向5
    piecePattern[9] = [
        [[0,0,1,0],[1,1,0,0],[0,1,0,0],[0,0,0,0]], # 方向0の[x][y]
        [[0,1,0,0],[0,1,1,0],[1,0,0,0],[0,0,0,0]], # 方向1
        [[0,0,1,0],[1,1,0,0],[0,1,0,0],[0,0,0,0]], # 方向2
        [[0,1,0,0],[0,1,1,0],[1,0,0,0],[0,0,0,0]], # 方向3
        [[0,0,1,0],[1,1,0,0],[0,1,0,0],[0,0,0,0]], # 方向4
        [[0,1,0,0],[0,1,1,0],[1,0,0,0],[0,0,0,0]]] # 方向5

    def __init__( self, parent ):
        self.main_frame = parent
        self.main_board = parent.board
        self.next_piece = parent.next_piece

    # ゲームの開始準備
    def PrepareStartGame( self ):
        self.playing = False
        self.pausing = False
        self.game_end = False

        # 盤面の初期化
        # 全部を白で塗る
        for y in range( FIELD_HEIGHT ):
            for x in range( FIELD_WIDTH ):
                self.field[x][y] = 0
        # 周囲1列を3で塗る
        for y in range( FIELD_HEIGHT ):
            self.field[0][y] = 3
        for x in range( FIELD_WIDTH ):
            self.field[x][ -int(x*0.5)+FIELD_HEIGHT - 1 ] = 3
        for y in range( FIELD_HEIGHT ):
            self.field[ FIELD_WIDTH-1 ][-int((FIELD_WIDTH-1)*0.5)+y] = 3

        # 1個目のピースを準備する
        self.GetNextPiece( True )
        self.RedrawAllField()

    # 1行だけ消す処理
    # offset: 0 or 1
    def DeleteOneLine( self, line, offset ):
        # 上から1行落とす
        # 下から上に向かって1行ずつ,上の行を下の行にコピーする
        for y in range(line, 1, -1):
            for x in range(1,FIELD_WIDTH-1):
                # offsetが0か1かでジグザグのパターンが変わる
                y0 = y-int((x+offset)*0.5)
                self.field[x][y0] = self.field[x][y0-1]
        self.RedrawAllField()

    # 埋まっている行を全て消す処理
    def DeleteFilledLines( self ):
        #print "Start Delete Line!!!"
        # yとoffsetで両方のパターンのジグザグのカウントをする
        y = FIELD_HEIGHT - 2 # 一番下の行を除く.
        offset = 0
        deleted_line = 0 # 削除できた行の数
        all_clear = 0 # 残りが0になった場合
        # 一番上の行まで確認
        count_sum = 0
        while y >= 0:
            # 1行に埋まってるセルの個数を数える
            #print "count line %d, offset %d:" % (y, offset),
            # 1行のカウント
            count = 0
            for x in range(1, FIELD_WIDTH-1): # 両端を除く
                #print "%d" % self.field[x][y-int((x+offset)*0.5)],
                # offsetが0か1かでジグザグのパターンが変わる
                if self.field[x][y-int((x+offset)*0.5)] == 2:
                    count = count + 1
            #print "count %d" % count
            # 全部が埋まっていたら行の削除処理
            if count == FIELD_WIDTH - 2: # 両端を除いた数
                # 削除できた場合にはyは増えない offsetも増えないのが良い
                #print "clear line %d" % y
                self.DeleteOneLine(y, offset)
                deleted_line = deleted_line + 1
            else:
                count_sum = count_sum + count
                # 全部が埋まっていなかったら次の行へ行く
                if offset == 0:
                    offset = 1
                else: # offset == 1
                    offset = 0
                    y = y - 1
        print "end Delete Line!!! (%d lines)" % deleted_line
        print "%d cells left (including double counting)" % count_sum

        # count_sumが0だったらボーナス
        if count_sum == 0:
            all_clear == 1
            print '########################################'
            print '######## ALL CELLS CLEARED #############'
            print '########################################'

    # 現在のピースが落ち切ったので,ここに固定する
    # 行が埋まったら消す
    # 次のピースを開始する
    def FixPieceAndGetNextPiece( self ):
        # 現在のピースを現在の位置に固定する
        for x in range(PIECE_WIDTH):
            for y in range(PIECE_HEIGHT):
                if self.piece[x][y] == 1:
                    self.field[self.location[0]+x][self.location[1]+y] = 2
        # 埋まっている行を消す
        self.DeleteFilledLines()
        # 次のピース
        self.GetNextPiece( False )
        self.RedrawAllField()

    # next pieceの領域に次のピースを作成する
    def CreatePiece( self ):
        self.id_next = random.randint(0, len(self.piecePattern)-1)
        #self.id_next = 9
        #print "piece pattern [%d]" % self.id_next,
        self.orientation = 0
        #print "piece orientation [%d]" % self.orientation
        # パターンi
        self.nextPiece = self.piecePattern[self.id_next]
        #print "-->", self.nextPiece
        #print "orientation 0 -->", self.nextPiece[0]
        # 向き0を表示する
        self.next_piece.DisplayPiecePattern( self.nextPiece[0] )

    # next pieceの領域からピースを持ってきて一番上に置く
    # 既にここまでピースが積んであったらゲームオーバー判定
    def GetNextPiece( self, is_first ):
        if is_first:
            self.CreatePiece()
        #print self.nextPiece
        self.location[0] = 5
        self.location[1] = -2
        # 次のピースを持ってくる(向きは0方向)
        self.id = self.id_next
        self.piece = self.piecePattern[self.id][0]
        for x in range( PIECE_WIDTH ):
            for y in range( PIECE_HEIGHT ):
                # フィールドに置く
                self.main_board.SetColor( self.location[0]+x,
                                          self.location[1]+y,
                                          self.piece[x][y])
        # ゲーム終了判定
        for y in range( PIECE_HEIGHT ):
            # 内側のxのloopから抜けて来ていたら,ここでも抜ける
            if self.game_end == True:
                break
            for x in range( PIECE_WIDTH ):
                # フィールドに置いた瞬間,既にその場所にブロックがあったら
                if ( self.piece[x][y] == 1 and
                     self.field[self.location[0]+x][self.location[1]+y] == 2):
                    # 終わり
                    self.game_end = True
                    # 次回でタイマーでのloopを止める
                    self.playing = False
                    # 1個重なっていれば十分なので,ここで抜ける
                    break
        if self.game_end == False:
            # 終わっていなかったら続ける
            self.CreatePiece()
        else:
            # ゲームオーバー処理の開始
            print "GAME END !!!"
            self.main_frame.endGame()

    def MoveOrientation( self, move ):
        xmove = ymove = 0
        if move == 'down':
            ymove = 1
        elif move == 'left':
            xmove = -1
            if self.location[0] % 2 == 1:
                ymove = 1
        elif move == 'right':
            xmove = 1
            if self.location[0] % 2 == 0:
                ymove = -1
        return ( xmove, ymove )

    def MovePiece( self, move ):
        ( xmove, ymove ) = self.MoveOrientation( move )
        self.location[0] = self.location[0] + xmove
        self.location[1] = self.location[1] + ymove

    def TurnPiece( self ):
        self.orientation = (self.orientation + 1) % self.ORIENT_NUM
        self.piece = self.piecePattern[self.id][self.orientation]

    # 移動した先を計算して,fieldとかぶらないか,はみ出ないかを見る
    # return 1: 左右の壁に当たって移動できない
    # return 2: 積んであるフィールド上のセルと重なるか,
    #           下に当たっているので,これで固定する
    def CanMovePiece( self, move='down' ):
        ( xmove, ymove ) = self.MoveOrientation( move )
        ( x0, y0 ) = ( self.location[0], self.location[1] )
        # フィールド上のセルと重なっている場合
        flag = 0
        for x in range(PIECE_WIDTH):
            for y in range(PIECE_HEIGHT):
                if ( self.piece[x][y] == 1 and
                     self.field[x0+x+xmove][y0+y+ymove] >= 1 ):
                    flag = 1
                    #print "move NG (4) (%d,%d)->(%d,%d)" % (
                    #    self.location[0]+x, self.location[1]+y,
                    #    self.location[0]+x+xmove, self.location[1]+y+ymove )
                    break
        if flag == 1:
            if ymove == 1:
                return 2 # 下に動けない場合はこれで固定
            else:
                return 1 # 左右ぶつかっている場合は,動けないだけ
        # 以上のチェックに引っかからない場合は動ける
        return 0

    # 回転した先を計算して,fieldとかぶらないか,はみ出ないかを見る
    # True: 回転できる
    def CanTurnPiece( self ):
        turnedPiece = [[0 for y in range(PIECE_HEIGHT)]
                       for x in range(PIECE_WIDTH)]
        o = (self.orientation + 1) % self.ORIENT_NUM
        turnedPiece = self.piecePattern[self.id][o]
        # 確認
        flag = 0
        for x in range(PIECE_WIDTH):
            for y in range(PIECE_HEIGHT):
                # 添字が範囲を越えているかどうかの判定
                xx = self.location[0] + x
                yy = self.location[1] + y
                if ( xx < 0 or xx >= FIELD_WIDTH ):
                    flag = 1
                    print "turn NG (1)"
                    break
                if ( yy < -int((self.location[0]+x) * 0.5) or
                     yy >= FIELD_HEIGHT - int((self.location[0]+x) * 0.5) ):
                    flag = 1
                    print "turn NG (2)"
                    break
                # 重なっている場合
                if ( turnedPiece[x][y] == 1 and
                     self.field[xx][yy] >= 1 ):
                    flag = 1
                    print "turn NG (3)"
                    break
        if flag == 1:
            return False
        else:
            return True

    # 再描画
    def RedrawAllField( self ):
        # ゲームの終了後は実行しない
        # 開始前は表示する
        if self.game_end == True:
            return
        #print "RedrawAllField"
        # まず固定セル
        for x in range( FIELD_WIDTH ):
            for y in range( - int(x * 0.5), FIELD_HEIGHT - int(x * 0.5) ):
                self.main_board.SetColor( x, y, self.field[x][y] )
        # 次に移動中のpiece
        for x in range( PIECE_WIDTH ):
            for y in range( PIECE_HEIGHT ):
                xx = x + self.location[0]
                yy = y + self.location[1]
                if xx < 0: pass
                if xx >= FIELD_WIDTH: pass
                if yy < -int(x * 0.5): pass
                if yy >= FIELD_HEIGHT - int(x * 0.5): pass
                # 描かれていない場所まで塗らないようにする
                if self.piece[x][y] == 1:
                    self.main_board.SetColor( xx, yy, 1 )

##------
if __name__ == '__main__':
    f = Frame()
    f.pack()
    f.mainloop()

# EOF

0 件のコメント:

コメントを投稿