LoginSignup
29
16

More than 5 years have passed since last update.

ぷよぷよ

Last updated at Posted at 2017-02-05

puyopuyo.gif
(beep(えいっ!) beep(ファイヤー) beep(アイスストーム) beep(ダイヤキュート) beep(ブレインダムド) beep(ジュゲム) beep(ばよえーん))

質素なテトリスと違い、ぷよぷよには可愛らしい絵や音がある
それらを取り除いた、ただのパズルゲームを作るのはそう難しくない
と思って作り始めると休日が終わっていた、ナンテコッタイ

おさらい

wikipedia

各作品によってルールの細部は異なる。ここでは基本的なルールのみを記す。
フィールドは縦12マス×横6マスの格子で構成される。格子の1マスにつき1個のブロック(ぷよぷよ略してぷよ)を置くことができる。ただし、上方向は、画面外に1マス分だけぷよを置くことができる。
上からぷよが2つ1組で落下してくる(「組ぷよ」と呼ばれる[注 4])。ぷよは種類ごとに色が異なり、色は3-5色(通常は4色)ある。プレイヤーはぷよに対して回転、横移動、高速落下のいずれかの操作を行う。
次に落下するぷよはフィールドの枠外にNEXTぷよとして予告される。配られるぷよの配分は麻雀のツモに例えられている。
落下してきたぷよがフィールドの床やほかのぷよに衝突すると、その位置にぷよが固定される。ただし、組ぷよを横にして置いたりなどして、ぷよに1マス分でも下方向に空白がある場合は、強制的にそのぷよだけ落下する。
固定されたぷよと同色の'ぷよ'が周囲4方向にいる場合、それらは互いにくっつく。
ぷよが4個以上くっつくと消滅し得点となる。
ぷよの消滅により上にあった'ぷよ'が落下する。このとき再びぷよが4個以上くっつくと消滅し、連鎖が起きる。なお、普通に4つ色を並べて消す行為だけでも1連鎖と考え、消滅した回数(○回)に応じて○連鎖と呼ばれる。複数色を同時に消した場合でも、1連鎖扱いとなる。
ぷよを消したときに入る得点は、消したぷよの数に、設定された「連鎖倍率」を掛けることで計算できる。
左から3列目が一番上まで埋まると"窒息して"ゲームオーバー。

今回はNEXTぷよとゲームオーバーは未実装、色は4色

PuyoPuyo

テトリス
これをベースにします
変更点は主に落下と消去
スコアやコンボ判定を追加

class PuyoPuyo 
  attr_accessor :puyo, :board
  def initialize(y,x)
    @board = Array.new(y) { Array.new(x) { 0 } }
    @puyo = rand_puyo
    @score = 0
    @comb = 0
  end

  def rand_puyo
    [[rand(1..4), [0,@board.size[0]/2]], [rand(1..4), [1,@board.size[0]/2]]]
  end

  def move?(ps)
    ps.all? do |y,x|
      @board.size > y && 0 <= y ?
        @board[y].size > x && 0 <= x ?
          @board[y][x] == 0 :
          false :
        false
    end
  end

  def rotate
    r = Math::PI / 2
    cy = @puyo.map { |c,(y,x)| y }.reduce(:+) / 2
    cx = @puyo.map { |c,(y,x)| x }.reduce(:+) / 2
    ps = @puyo.map do |c,(y,x)|
      [
        (cy + (x - cx) * Math.sin(r) + (y - cy) * Math.cos(r)).round,
        (cx + (x - cx) * Math.cos(r) - (y - cy) * Math.sin(r)).round
      ]
    end
    p ps

    if move?(ps)
      @puyo[0][1] = ps[0]
      @puyo[1][1] = ps[1]
    end
  end

  def down
    ps = @puyo.map { |c,(y,x)| [y + 1, x] }
    if move?(ps)
      @puyo[0][1] = ps[0]
      @puyo[1][1] = ps[1]
    end
  end

  def right
    ps = @puyo.map { |c,(y,x)| [y, x + 1] }
    if move?(ps)
      @puyo[0][1] = ps[0]
      @puyo[1][1] = ps[1]
    end
  end


  def left
    ps = @puyo.map { |c,(y,x)| [y, x - 1] }
    if move?(ps)
      @puyo[0][1] = ps[0]
      @puyo[1][1] = ps[1]
    end
  end


  def puyo_fall
    ps = @puyo.map { |c,(y,x)| [y + 1, x] }
    if move?(ps)
      @puyo[0][1] = ps[0]
      @puyo[1][1] = ps[1]
    else
      @board[@puyo[0][1][0]][@puyo[0][1][1]] = @puyo[0][0]
      @board[@puyo[1][1][0]][@puyo[1][1][1]] = @puyo[1][0]
      @puyo = rand_puyo
    end
  end

  def board_fall
    for y in (@board.size - 1).downto(0)
      for x in 0 .. @board[y].size - 1
        if @board[y][x] == 0
          a = 0
          for yy in y.downto(0)
            if @board.size > yy && 0 <= yy && @board[yy][x] > 0
              @board[y-a][x] = @board[yy][x]
              @board[yy][x] = 0
              a += 1
            end
          end
        end
      end
    end
  end

  def board_delete
    for y in 0 .. @board.size - 1
      for x in 0 .. @board[y].size - 1
        if @board[y][x] > 0
          ss = [[y,x]]
          ds = [[y,x]]
          while !ss.empty?
            sy,sx = ss.pop
            for ay,ax in [[-1,0],[1,0],[0,-1],[0,1]]
              yy = ay + sy
              xx = ax + sx
              if @board.size > yy && 0 <= yy && @board[yy].size > xx && 0 <= xx &&
                  @board[yy][xx] == @board[y][x] && !ds.any? { |a| a == [yy,xx] }
                ss.push([yy,xx])
                ds.push([yy,xx])
              end
            end
          end

          if ds.size >= 4
            for dy,dx in ds
              @board[dy][dx] = 0
            end
            @comb += 1
            @score += ds.size * @comb
          end
        end
      end
    end
  end

  require "curses"
  C = Curses

  def controller(c)
    case c
    when "w"
      rotate
    when "s"
      down
    when "d"
      right
    when "a"
      left
    when "q"
      C.close_screen
      exit
    else
      nil
    end
  end

  def display_init
    C.init_screen
    C.start_color
    C.use_default_colors
    C.noecho
    C.curs_set(0)

    [
      C::COLOR_BLACK,
      C::COLOR_RED,
      C::COLOR_GREEN,
      C::COLOR_YELLOW,
      C::COLOR_BLUE,
    ].each.with_index do |c,i|
      C.init_pair(i, C::COLOR_BLACK, c)
    end
  end

  def display
    C.clear
    C.addstr("-" * (@board[0].size + 2))
    C.addstr("\n")
    for y in 0 .. @board.size - 1
      C.addstr("|")
      for x in 0 .. @board[y].size - 1
        c = @puyo[0][1] == [y,x] ? @puyo[0][0] :
          @puyo[1][1] == [y,x] ? @puyo[1][0] : @board[y][x]
        C.attron(C.color_pair(c))
        C.addstr(" ")
        C.attroff(C.color_pair(c))
      end
      C.addstr("|")
      C.addstr("\n")
    end
    C.addstr("-" * (@board[0].size + 2))
    C.addstr("\nscore: #{@score}\ncomb: #{@comb}\n")
    C.refresh
  end

  def run
    display_init
    display

    m = Mutex.new

    def delete
      @comb = 0
      c = @comb
      board_delete
      while @comb != c
        #C.flash
        C.beep
        c = @comb
        board_fall
        display

        sleep 0.5
        board_delete
        display
      end
    end

    Thread.new do
      loop do 
        sleep 1
        m.synchronize do
          puyo_fall
          board_fall
          display
          sleep 0.5
          delete
          display
        end
      end
    end

    loop do 
      c = C.getch.to_s
      m.synchronize do
        controller(c)
        board_fall
        display
        delete
        display
      end
    end
  end
end

pp = PuyoPuyo.new(12,6)

#board = <<END
#4
#1
#4
#32132
#21321
#321321
#321321
#344242
#423122
#142314
#142314
#142314
#END
#
#pp.puyo = [[1,[0,5]],[1,[1,5]]]
#
#bs = board.split(/\n/).map { |b| b.split(//) }
#bs.each_with_index do |b,y|
#  b.each_with_index do |c,x|
#    pp.board[y][x] = c.to_i
#  end
#end
#

pp.run

end

cpu対戦やネットワーク対戦があるとだいぶ楽しくなりそう(作らない)

29
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
16