0

現在、私たちのコードは、左上から開始し、行と列を左から右に行ごとに埋めるグリッドを作成します。現在、選択できる画像がたくさんあります。これは、シェイプとレアシェイプを選択する少数の IF ステートメントを使用して設定されます。私がどのようにやるかを理解しようとしているのは、コードを変更して、ランダムなレアシェイプを選択する代わりに、どのレアシェイプがスポーンするかを決定できるようにすることです。まだ Python は初めてで、他の言語から私にとって意味のある多くの小さなことを見つけることは、Python では機能しないため、少し気が遠くなります。

編集:

これが完全なコードです。cactusbin によって書かれ、Gareth Rees によって改訂された基本コードの功績。

import pygame, random, time, sys
from pygame.locals import *
import itertools
import os

WHITE = (255, 255, 255)
BLACK = (0, 0, 0)

SHAPE_WIDTH = 64                # Width of each shape (pixels).
SHAPE_HEIGHT = 64               # Height of each shape (pixels).
PUZZLE_COLUMNS = 10              # Number of columns on the board.
PUZZLE_ROWS = 11                # Number of rows on the board.
MARGIN = 118                     # Margin around the board (pixels).
WINDOW_WIDTH = PUZZLE_COLUMNS * SHAPE_WIDTH + 2 * MARGIN + 485
WINDOW_HEIGHT = PUZZLE_ROWS * SHAPE_HEIGHT + 2 * MARGIN - 150
FONT_SIZE = 60
TEXT_OFFSET = MARGIN + 950

# Map from number of matches to points scored.
MINIMUM_MATCH = 10
EXTRA_LENGTH_POINTS = .1
RANDOM_POINTS = .3
DELAY_PENALTY_SECONDS = 1
DELAY_PENALTY_POINTS = 0

FPS = 30
EXPLOSION_SPEED = 15            # In frames per second.
SPIN_SPEED = 15
REFILL_SPEED = 10               # In cells per second.

VERTICAL = False

class Cell(object):
"""
A cell on the board, with properties:
`image` -- a `Surface` object containing the sprite to draw here.
`offset` -- vertical offset in pixels for drawing this cell.
"""
def __init__(self, image):
    self.offset = 0.0
    self.image = image

def tick(self, dt):
    self.offset = max(0.0, self.offset - dt * REFILL_SPEED)

class Board(object):
"""
A rectangular board of cells, with properties:
`w` -- width in cells.
`h` -- height in cells.
`size` -- total number of cells.
`board` -- list of cells.
`matches` -- list of matches, each being a list of exploding cells.
`refill` -- list of cells that are moving up to refill the board.
`score` -- score due to chain reactions.
"""
def __init__(self, width, height):
    self.explosion = [pygame.image.load('images/explosion{}.png'.format(i))
                      for i in range(1, 7)]
    self.spin = [pygame.image.load('images/powerframe{}.png'.format(i))
                  for i in range (1, 12)]
    self.image_color = {}
    self.shapes = []
    self.rareshapes = []

    colors = 'red blue yellow'
    letters = 'acgtu'

    for c in colors.split():
        im = pygame.image.load('images/{}.png'.format(c))
        self.shapes.append(im)
        self.image_color[im] = c
        for l in letters:
            im = pygame.image.load('rareimages/{}{}.png'.format(c, l))
            self.rareshapes.append(im)
            self.image_color[im] = l

    self.background = pygame.image.load("images/bg.png")
    self.blank = pygame.image.load("images/blank.png")
    self.x = pygame.image.load("images/x.png")
    self.w = width
    self.h = height
    self.size = width * height
    self.board = [Cell(self.blank) for _ in range(self.size)]
    self.matches = []
    self.refill = []
    self.score = 0.0
    self.spin_time = 15


def randomize(self):
    """
    Replace the entire board with fresh shapes.
    """
    rare_shapes = [1, 9, 23, 27, 40, 42, 50, 56, 70, 81, 90]

    for i in range(self.size):
        if i in rare_shapes:
            self.board[i] = Cell(random.choice(self.rareshapes))
        else:
            self.board[i] = Cell(random.choice(self.shapes))

def pos(self, i, j):
    """
    Return the index of the cell at position (i, j).
    """
    assert(0 <= i < self.w)
    assert(0 <= j < self.h)
    return j * self.w + i

def busy(self):
    """
    Return `True` if the board is busy animating an explosion or a
    refill and so no further swaps should be permitted.
    """
    return self.refill or self.matches

def tick(self, dt):
    """
    Advance the board by `dt` seconds: move rising blocks (if
    any); otherwise animate explosions for the matches (if any);
    otherwise check for matches.
    """
    if self.refill:
        for c in self.refill:
            c.tick(dt)
        self.refill = [c for c in self.refill if c.offset > 0]
        if self.refill:
            return
    elif self.matches:
        self.explosion_time += dt
        f = int(self.explosion_time * EXPLOSION_SPEED)
        if f < len(self.explosion):
            self.update_matches(self.explosion[f])
            return
        self.update_matches(self.blank)
        self.refill = list(self.refill_columns())
    self.explosion_time = 0
    self.matches = self.find_matches()

def draw(self, display):
    """
    Draw the board on the pygame surface `display`.
    """
    display.blit(self.background, (0, 0))
    for i, c in enumerate(self.board):
        display.blit(c.image,
                     (MARGIN + SHAPE_WIDTH * (i % self.w),
                      MARGIN + SHAPE_HEIGHT * (i // self.w - c.offset) - 68))
    display.blit(self.x, (995, 735))
    display.blit(self.x, (1112, 735))
    display.blit(self.x, (1228, 735))

def swap(self, cursor):
    """
    Swap the two board cells covered by `cursor` and update the
    matches.
    """
    i = self.pos(*cursor)
    b = self.board
    b[i], b[i+1] = b[i+1], b[i]
    self.matches = self.find_matches()


def find_matches(self):
    """
    Search for matches (lines of cells with identical images) and
    return a list of them, each match being represented as a list
    of board positions.
    """
    def lines():
        for j in range(self.h):
            yield range(j * self.w, (j + 1) * self.w)
        for i in range(self.w):
            yield range(i, self.size, self.w)
    def key(i):
        return self.image_color.get(self.board[i].image)
    def matches():
        for line in lines():
            for _, group in itertools.groupby(line, key):
                match = list(group)
                if len(match) >= MINIMUM_MATCH:
                    yield match
                    self.score = self.score + 1
    return list(matches())

def update_matches(self, image):
    """
    Replace all the cells in any of the matches with `image`.
    """
    for match in self.matches:
        for position in match:
            self.board[position].image = image

def refill_columns(self):
    """
    Move cells downwards in columns to fill blank cells, and
    create new cells as necessary so that each column is full. Set
    appropriate offsets for the cells to animate into place.
    """
    for i in range(self.w):
        target = self.size - i - 1
        for pos in range(target, -1, -self.w):
            if self.board[pos].image != self.blank:
                c = self.board[target]
                c.image = self.board[pos].image
                c.offset = (target - pos) // self.w
                target -= self.w
                yield c
        offset = 1 + (target - pos) // self.w
        for pos in range(target, -1, -self.w):
            c = self.board[pos]
            c.image = random.choice(self.shapes)
            c.offset = offset
            yield c

class Game(object):
"""
The state of the game, with properties:
`clock` -- the pygame clock.
`display` -- the window to draw into.
`font` -- a font for drawing the score.
`board` -- the board of cells.
`cursor` -- the current position of the (left half of) the cursor.
`score` -- the player's score.
`last_swap_ticks` -- 
`swap_time` -- time since last swap (in seconds).
"""
def __init__(self):
    pygame.init()
    pygame.display.set_caption("Nucleotide")
    self.clock = pygame.time.Clock()
    self.display = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT),
                                           DOUBLEBUF)
    self.board = Board(PUZZLE_COLUMNS, PUZZLE_ROWS)
    self.font = pygame.font.Font(None, FONT_SIZE)

def start(self):
    """
    Start a new game with a random board.
    """
    self.board.randomize()
    self.cursor = [0, 0]
    self.score = 0.0
    self.swap_time = 125

def quit(self):
    """
    Quit the game and exit the program.
    """
    pygame.quit()
    sys.exit()

def play(self):
    """
    Play a game: repeatedly tick, draw and respond to input until
    the QUIT event is received.
    """
    self.start()
    while True:
        self.draw()
        dt = min(self.clock.tick(FPS) / 1000, 1 / FPS)
        self.swap_time -= dt
        for event in pygame.event.get():
            if event.type == KEYUP:
                self.input(event.key)
            elif event.type == QUIT:
                self.quit()
            elif self.swap_time == 0:
                self.quit()
        self.board.tick(dt)

def input(self, key):
    """
    Respond to the player pressing `key`.
    """
    if key == K_q:
        self.quit()
    elif key == K_RIGHT and self.cursor[0] < self.board.w - 2:
        self.cursor[0] += 1
    elif key == K_LEFT and self.cursor[0] > 0:
        self.cursor[0] -= 1
    elif key == K_DOWN and self.cursor[1] < self.board.h - 1:
        self.cursor[1] += 1
    elif key == K_UP and self.cursor[1] > 0:
        self.cursor[1] -= 1
    elif key == K_SPACE and not self.board.busy():
        self.swap()

def swap(self):
    """
    Swap the two cells under the cursor and update the player's score.
    """
    self.board.swap(self.cursor)

def draw(self):
    self.board.draw(self.display)
    self.draw_score()
    self.draw_time()
    if VERTICAL == False:
        self.draw_cursor()
    elif VERTICAL == True:
        self.draw_cursor2()
    pygame.display.update()

def draw_time(self):
    s = int(self.swap_time)
    text = self.font.render(str(int(s/60)) + ":" + str(s%60).zfill(2),
                            True, BLACK)
    self.display.blit(text, (TEXT_OFFSET, WINDOW_HEIGHT - 170))

def draw_score(self):
    total_score = self.score

def draw_cursor(self):
    topLeft = (MARGIN + self.cursor[0] * SHAPE_WIDTH,
            MARGIN + self.cursor[1] * SHAPE_HEIGHT - 68)
    topRight = (topLeft[0] + SHAPE_WIDTH * 2, topLeft[1])
    bottomLeft = (topLeft[0], topLeft[1] + SHAPE_HEIGHT)
    bottomRight = (topRight[0], topRight[1] + SHAPE_HEIGHT)
    pygame.draw.lines(self.display, WHITE, True,
            [topLeft, topRight, bottomRight, bottomLeft], 3)

if __name__ == '__main__':
    Game().play()
4

2 に答える 2

1

あなたが求めているのが、通常のシェイプの代わりにレア シェイプを配置する必要がある、rareshapecount の間隔をより簡単に指定する方法である場合は、次のアプローチがより読みやすくなります。

def randomize(self):
   """
   Replace the entire board with fresh shapes.
   """
   # locations we want to place a rare shape
   rare_shapes = [9, 23, 27]

   for i in range(self.size):
      if i in rare_shapes:
         self.board[i] = Cell(random.choice(self.rareshapes))
      else:
         self.board[i] = Cell (random.choice(self.shapes))

必要に応じて、間隔を毎回ハードコーディングしたくない場合は、rare_shapes をランダムに設定することもできます。これにより、より多様な体験が可能になります (つまり、ゲームなどを設計している場合)。

于 2013-10-30T16:37:18.030 に答える
0

「ランダムなレアシェイプを選ぶのではなく、どのレアシェイプがスポーンするかを決めることができる」とはどういう意味ですか? もっと説明をお願いできますか?どのレアシェイプを使用するかをプログラムにどのように伝えるでしょうか?

それまでの間、コードのより Pythonic なバージョンを以下に示します。

def randomize(self):
    """
    Replace the entire board with fresh shapes.
    """

    specials = dict((x, self.rareshapes) for x in (9, 23, 27))
    get_shape_source = lambda x: specials.get(x, self.shapes) 

    for i in xrange(min(self.size, 41)):
        self.board[i] = Cell(random.choice(get_shape_source(i)))

len(self.board) < min(self.size, 41)これはうまくいかないことに注意してください。

編集:あなたのコメントを考えると、どのレアシェイプがどこに行くのかを明示的に選択する明白な方法は、画像を明示的にスポットに関連付けることです。これを行うための最良の方法/これを構成するための最良の場所は、実際にはコード全体、または少なくとも投稿したもの以上に依存します。非常に単純で最小限の例として、次のようにすることができます。

from collections import ordereddict

def load_images(self)  
    self.image_color = {}
    self.shapes = []
    self.rareshapes = ordereddict()

    colors = 'red',  'blue', 'yellow'
    letters = 'acgtu'

    for c in colors:
        im = pygame.image.load('images/{}.png'.format(c))
        self.shapes.append(im)
        self.image_color[im] = c
        for l in letters:
            im = pygame.image.load('rareimages/{}{}.png'.format(c, l))
            self.rareshapes.[(c, l)] = im
            self.image_color[im] = l

def randomize(self):
    """
    Replace the entire board with fresh shapes.
    """

    raremap = {
       # spot index : rareshape
       9: ('red', 'a')],  
       23: ('blue', 'u'), 
       27: ('yellow', 'g') 
       }

    for i in xrange(self.size):
        if i in raremap:
            im = self.rareshapes[raremap[i]]
        else:
            im = random.choice(self.shapes)
        self.board[i] = Cell(im)

しかし、長い目で見れば、それは単にメンテナンスが不可能になるでしょう - ハードコードされたものが多すぎて、ある方法から別の方法に漏れる知識が多すぎます。「自己」が何のインスタンスかはわかりませんが、責任を分割して、不変部分をあるクラスに、「構成」(ロードする画像、スポット/レアシェイプのマッピングなど) を別のクラスに持つことを検討する必要があります。頭に浮かぶいくつかの設計パターンは、TemplateMethod(不変部分と「構成」部分を実装する具体的なサブクラスを持つ抽象基本クラスがある場合)、ビルダー、そしてもちろん戦略(あなたの場合、「戦略」クラスが世話をします構成の)。

于 2013-10-30T16:25:01.830 に答える