PuyoPuyoChap1
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
[[目次へもどる>PuyoPuyo]]
* FieldとBlock [#q44c70ef]
#contents
** Block [#d245f240]
まず,ぷよぷよは商標登録だと思われるので
ぷよのことを「ブロック」と呼ぶことにしよう.
実際開発段階ではただの矩形で表示するし.
まあ結局いたるところでぷよぷよぷよぷよ連呼するんだけどね!
このブロックを表すクラス「Block」を定義した.
#sh(ruby){{
class Block
attr_accessor :color, :row, :line
def initialize(col)
@color = col
@row = -1
@line = -1
end
def inspect
sprintf("|(%2d,%2d):%s|",@row,@line,@color.to_s)
end
def to_s
sprintf("|(%2d,%2d):%s|",@row,@line,@color.to_s)
end
end
}}
まずブロックは色,列,行をプロパティに持つ.なんで行,列...
今にわかります.
また,デバッグのために「to_s」と「inspect」メソッドをオー...
これでブロックの位置と色がわかりやすく表示される.
** Field [#o917b6ea]
次にブロックが配置される場所が必要だ.
ぷよぷよで言うところのキャラ絵があるところ.
これを我々はフィールドと呼ぶことにする.
#sh(ruby){{
class Field
end
}}
*** table [#aac2d968]
フィールドは普通6列の12行(だっけ?)で構成されるタイル状の...
なのでそれをテーブルと呼ぶことにして定義しよう.
#sh(ruby){{
def initialize(row_s, line_s)
@row_s = row_s
@line_s = line_s
init_table
end
def init_table
@table = []
@row_s.times do |row|
@table.push Array.new(@line_s)
end
end
}}
なんで@row_s,@line_sを受け取るようにしてるかといえばもち...
ちびぷよでプレイとか実現できるかもしれない.
テーブルの構造は至って簡単.配列の配列である.
まず列数の要素を持つ配列を作成し,その要素それぞれに行数...
なんで行,列じゃなくて列,行かだって?
今にわかります.
テーブルの初期値はnilなので,
@table[r][l]がnilならそのマスにブロックは無いことになる.
ちなみにrowは左から0,1,2,3,4,5である.
lineは下から0,1,2,3,4,5...である.
*** blocklist [#oa46f32d]
さて,テーブルを作ってそこにブロックを格納していくのはい...
フィールドにあるすべてのブロックになんらかの処理を行いた...
そこで,テーブルとは別にブロックの一覧を配列で持つことに...
んでもってこれらを利用してフィールドにブロックを配置する...
#sh(ruby){{
def initialize(row_s, line_s)
@row_s = row_s
@line_s = line_s
init_table
init_blocklist
end
def init_table
@table = []
@row_s.times do |row|
@table.push Array.new(@line_s)
end
end
def init_blocklist
@blocklist = []
end
def set(r,l,col)
if @table[r][l]
@blocklist.delete @table[r][l]
end
block = Block.new(col)
block.row = r
block.line = l
@table[r][l] = block
@blocklist.push block
end
}}
(注意)テーブルにブロックを格納といったが,Rubyではすべて...
@table[r][l] = block; @blocklist.push block
とした場合,それぞれ同じblockを参照することになる.
** ブロックを消す [#a1a9004c]
さあ,ブロックをフィールドに配置できたらとりあえずやるこ...
ブロックを消しましょう.
*** つながってる? [#id19c5f1]
ブロックを消す条件は
- 同じ色のブロックが上下左右に4個以上つながっている~
これである.
これをなるべくいい感じに行う方法を考えた.
+ まずフィールド上のブロックすべてをチェック対象としてチ...
+ つながっているブロックを管理するためのコネクトテーブル...
コネクトテーブルは「つながっているブロックの配列」の配列
+ チェックリストの先頭のブロックを取得
+ そのブロックを起点にしてつながりチェックを開始~
このとき新たにコネクトテーブルに空配列の要素を追加
+ ブロックがテーブルの範囲外ならreturn
+ ブロックがチェックリストに入っていなかったらすでにチェ...
+ ブロックの色が起点ブロックと異なっていたらreturn
+ ここまで来たらブロックがつながっているのでブロックをチ...
+ 現在のコネクトテーブルに追加
+ 追加したブロックを起点ブロックとして上下左右を6から再帰...
+ チェックリストがまだ残ってるなら3に戻る
この流れで二個以上つながっているブロックのグループがコネ...
#sh(ruby){{
def check_connection(r,l,col)
return if r < 0 || r >= @row_s
return if l < 0 || l >= @line_s
block = @table[r][l]
return unless @checklist.include? block
return unless @table[r][l] && @table[r][l].color == col
@checklist.delete block
@connect_table[-1].push block
check_connection(r,l+1,col) # up
check_connection(r,l-1,col) # down
check_connection(r+1,l,col) # right
check_connection(r-1,l,col) # left
end
def make_connect_table
init_connect_table
@checklist = @blocklist.dup
while !@checklist.empty?
block = @checklist.first
r = block.row; l = block.line
col = block.color
@connect_table.push []
check_connection(r,l,col)
end
end
}}
*** 消す [#o844b741]
これは簡単.先ほど取得したつながってるグループのサイズが4...
#sh(ruby){{
def eliminate_connection
@connect_table.each do |connection|
next if connection.size < 4
@eliminated = true # check flag
connection.each do |block|
@table[block.row][block.line] = nil
@blocklist.delete block
end
end
end
}}
** 落ちる [#d6c81823]
さて,ブロックが消えたら当然スキマスイッチが出てくるので...
スキマスイッチとは以下のような状態のスキマのことである....
b
←ここ
b
----
重力で落ちるゲームというくらいだから隙間の上にあるブロッ...
まず当然のことながら各行ごとに考えれば十分なのでこんなん...
#sh(ruby){{
def falldown
@fallen = false
@row_s.times do |r|
falldown_line(r)
end
@fallen
end
}}
ちなみに@fallenってのは落ちたらtrue,変化なかったらfalse...
当然のことながらって言ったけど,これが先ほどから言っている
> なんで行,列じゃなくて列,行かだって?
今にわかります.
の答えである.まず最初に列で分割できたほうが何かと都合が...
まあ表示するときは先に行で分割したいからめんどくさいこと...
次に各行ごとの落下処理を考える.
まず行のサイズだけインデックスを回して(0〜11),最初にブロ...
ようするにこのベースがスキマスイッチの候補である.
そしたらベースの一個上のマスからまたしてもインデックスを...
あとはストップした位置以降すべてのマスをベースまでおろし...
これでおっけーかと思いきや,同じ列にスキマスイッチが一人...
ようするに先ほどベースだったマスの一つ上からもう一度ブロ...
これでこの行についてすべてのスキマスイッチが埋まる.
#sh(ruby){{
def falldown_line(r)
@line_s.times do |base|
next if @table[r][base]
# fall down to base
((base+1)...@line_s).each do |b_line|
next unless @table[r][b_line] # skip space sequence
@fallen = true # check flag
@table[r].slice!(base...b_line) # fall down
@table[r].concat(Array.new(b_line-base)) # fill e...
# update block pos
(base...@line_s).each do |l|
next unless @table[r][l]
@table[r][l].row = r
@table[r][l].line = l
end
break
end
end
end
}}
** ここまでをテスト [#y200f967]
ここまで正常に動作するかテストコードを実行してみよう!
$ ruby field.rb
さりげなく文字列から一気にフィールドにブロックを配置でき...
#sh(ruby){{
if __FILE__ == "field.rb"
field = Field.new(6,12)
tstr =<<EOF
......
b.....
y.....
b.....
b.r...
r.b...
r....y
bbb.yg
rygrbg
rbygrb
bygrbg
bygrbg
EOF
field.set_table(tstr)
field.print_field
loop do
fallen = field.falldown
field.print_field if fallen
eliminated = field.eliminate
field.print_field if eliminated
break if !fallen && !eliminated
end
}}
落下もしくは消去が起きなくなるまでループが続く.
これを実行すると以下の出力が得られる.
長いな.
--------
| |
|b |
|y |
|b |
|b r |
|r b |
|r y|
|bbb yg|
|rygrbg|
|rbygrb|
|bygrbg|
|bygrbg|
--------
--------
| |
|b |
|y |
|b |
|b |
|r r |
|r b y|
|bbb yg|
|rygrbg|
|rbygrb|
|bygrbg|
|bygrbg|
--------
--------
| |
|b |
|y |
|b |
|b |
|r r |
|r y|
| yg|
|rygrbg|
|rbygrb|
|bygrbg|
|bygrbg|
--------
--------
| |
| |
|b |
|y |
|b |
|b |
|r y|
|r r yg|
|rygrbg|
|rbygrb|
|bygrbg|
|bygrbg|
--------
--------
| |
| |
|b |
|y |
|b |
|b |
| y|
| r yg|
| ygrbg|
| bygrb|
|bygrbg|
|bygrbg|
--------
--------
| |
| |
| |
| |
| |
| |
|b y|
|y r yg|
|bygrbg|
|bbygrb|
|bygrbg|
|bygrbg|
--------
--------
| |
| |
| |
| |
| |
| |
|b y|
|y r yg|
| ygrbg|
| ygrb|
| ygrbg|
| ygrbg|
--------
--------
| |
| |
| |
| |
| |
| |
| y|
| r yg|
| grbg|
| yygrb|
|bygrbg|
|yygrbg|
--------
--------
| |
| |
| |
| |
| |
| |
| y|
| r yg|
| grbg|
| grb|
|b grbg|
| grbg|
--------
--------
| |
| |
| |
| |
| |
| |
| y|
| yg|
| rrbg|
| ggrb|
| grbg|
|b grbg|
--------
--------
| |
| |
| |
| |
| |
| |
| y|
| yg|
| rrbg|
| rb|
| rbg|
|b rbg|
--------
--------
| |
| |
| |
| |
| |
| |
| y|
| yg|
| bg|
| rrb|
| rbg|
|b rrbg|
--------
--------
| |
| |
| |
| |
| |
| |
| y|
| yg|
| bg|
| b|
| bg|
|b bg|
--------
--------
| |
| |
| |
| |
| |
| |
| y|
| g|
| yg|
| bb|
| bg|
|b bg|
--------
--------
| |
| |
| |
| |
| |
| |
| y|
| g|
| yg|
| |
| g|
|b g|
--------
--------
| |
| |
| |
| |
| |
| |
| |
| y|
| g|
| g|
| g|
|b yg|
--------
--------
| |
| |
| |
| |
| |
| |
| |
| y|
| |
| |
| |
|b y |
--------
--------
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|b yy|
--------
終了行:
[[目次へもどる>PuyoPuyo]]
* FieldとBlock [#q44c70ef]
#contents
** Block [#d245f240]
まず,ぷよぷよは商標登録だと思われるので
ぷよのことを「ブロック」と呼ぶことにしよう.
実際開発段階ではただの矩形で表示するし.
まあ結局いたるところでぷよぷよぷよぷよ連呼するんだけどね!
このブロックを表すクラス「Block」を定義した.
#sh(ruby){{
class Block
attr_accessor :color, :row, :line
def initialize(col)
@color = col
@row = -1
@line = -1
end
def inspect
sprintf("|(%2d,%2d):%s|",@row,@line,@color.to_s)
end
def to_s
sprintf("|(%2d,%2d):%s|",@row,@line,@color.to_s)
end
end
}}
まずブロックは色,列,行をプロパティに持つ.なんで行,列...
今にわかります.
また,デバッグのために「to_s」と「inspect」メソッドをオー...
これでブロックの位置と色がわかりやすく表示される.
** Field [#o917b6ea]
次にブロックが配置される場所が必要だ.
ぷよぷよで言うところのキャラ絵があるところ.
これを我々はフィールドと呼ぶことにする.
#sh(ruby){{
class Field
end
}}
*** table [#aac2d968]
フィールドは普通6列の12行(だっけ?)で構成されるタイル状の...
なのでそれをテーブルと呼ぶことにして定義しよう.
#sh(ruby){{
def initialize(row_s, line_s)
@row_s = row_s
@line_s = line_s
init_table
end
def init_table
@table = []
@row_s.times do |row|
@table.push Array.new(@line_s)
end
end
}}
なんで@row_s,@line_sを受け取るようにしてるかといえばもち...
ちびぷよでプレイとか実現できるかもしれない.
テーブルの構造は至って簡単.配列の配列である.
まず列数の要素を持つ配列を作成し,その要素それぞれに行数...
なんで行,列じゃなくて列,行かだって?
今にわかります.
テーブルの初期値はnilなので,
@table[r][l]がnilならそのマスにブロックは無いことになる.
ちなみにrowは左から0,1,2,3,4,5である.
lineは下から0,1,2,3,4,5...である.
*** blocklist [#oa46f32d]
さて,テーブルを作ってそこにブロックを格納していくのはい...
フィールドにあるすべてのブロックになんらかの処理を行いた...
そこで,テーブルとは別にブロックの一覧を配列で持つことに...
んでもってこれらを利用してフィールドにブロックを配置する...
#sh(ruby){{
def initialize(row_s, line_s)
@row_s = row_s
@line_s = line_s
init_table
init_blocklist
end
def init_table
@table = []
@row_s.times do |row|
@table.push Array.new(@line_s)
end
end
def init_blocklist
@blocklist = []
end
def set(r,l,col)
if @table[r][l]
@blocklist.delete @table[r][l]
end
block = Block.new(col)
block.row = r
block.line = l
@table[r][l] = block
@blocklist.push block
end
}}
(注意)テーブルにブロックを格納といったが,Rubyではすべて...
@table[r][l] = block; @blocklist.push block
とした場合,それぞれ同じblockを参照することになる.
** ブロックを消す [#a1a9004c]
さあ,ブロックをフィールドに配置できたらとりあえずやるこ...
ブロックを消しましょう.
*** つながってる? [#id19c5f1]
ブロックを消す条件は
- 同じ色のブロックが上下左右に4個以上つながっている~
これである.
これをなるべくいい感じに行う方法を考えた.
+ まずフィールド上のブロックすべてをチェック対象としてチ...
+ つながっているブロックを管理するためのコネクトテーブル...
コネクトテーブルは「つながっているブロックの配列」の配列
+ チェックリストの先頭のブロックを取得
+ そのブロックを起点にしてつながりチェックを開始~
このとき新たにコネクトテーブルに空配列の要素を追加
+ ブロックがテーブルの範囲外ならreturn
+ ブロックがチェックリストに入っていなかったらすでにチェ...
+ ブロックの色が起点ブロックと異なっていたらreturn
+ ここまで来たらブロックがつながっているのでブロックをチ...
+ 現在のコネクトテーブルに追加
+ 追加したブロックを起点ブロックとして上下左右を6から再帰...
+ チェックリストがまだ残ってるなら3に戻る
この流れで二個以上つながっているブロックのグループがコネ...
#sh(ruby){{
def check_connection(r,l,col)
return if r < 0 || r >= @row_s
return if l < 0 || l >= @line_s
block = @table[r][l]
return unless @checklist.include? block
return unless @table[r][l] && @table[r][l].color == col
@checklist.delete block
@connect_table[-1].push block
check_connection(r,l+1,col) # up
check_connection(r,l-1,col) # down
check_connection(r+1,l,col) # right
check_connection(r-1,l,col) # left
end
def make_connect_table
init_connect_table
@checklist = @blocklist.dup
while !@checklist.empty?
block = @checklist.first
r = block.row; l = block.line
col = block.color
@connect_table.push []
check_connection(r,l,col)
end
end
}}
*** 消す [#o844b741]
これは簡単.先ほど取得したつながってるグループのサイズが4...
#sh(ruby){{
def eliminate_connection
@connect_table.each do |connection|
next if connection.size < 4
@eliminated = true # check flag
connection.each do |block|
@table[block.row][block.line] = nil
@blocklist.delete block
end
end
end
}}
** 落ちる [#d6c81823]
さて,ブロックが消えたら当然スキマスイッチが出てくるので...
スキマスイッチとは以下のような状態のスキマのことである....
b
←ここ
b
----
重力で落ちるゲームというくらいだから隙間の上にあるブロッ...
まず当然のことながら各行ごとに考えれば十分なのでこんなん...
#sh(ruby){{
def falldown
@fallen = false
@row_s.times do |r|
falldown_line(r)
end
@fallen
end
}}
ちなみに@fallenってのは落ちたらtrue,変化なかったらfalse...
当然のことながらって言ったけど,これが先ほどから言っている
> なんで行,列じゃなくて列,行かだって?
今にわかります.
の答えである.まず最初に列で分割できたほうが何かと都合が...
まあ表示するときは先に行で分割したいからめんどくさいこと...
次に各行ごとの落下処理を考える.
まず行のサイズだけインデックスを回して(0〜11),最初にブロ...
ようするにこのベースがスキマスイッチの候補である.
そしたらベースの一個上のマスからまたしてもインデックスを...
あとはストップした位置以降すべてのマスをベースまでおろし...
これでおっけーかと思いきや,同じ列にスキマスイッチが一人...
ようするに先ほどベースだったマスの一つ上からもう一度ブロ...
これでこの行についてすべてのスキマスイッチが埋まる.
#sh(ruby){{
def falldown_line(r)
@line_s.times do |base|
next if @table[r][base]
# fall down to base
((base+1)...@line_s).each do |b_line|
next unless @table[r][b_line] # skip space sequence
@fallen = true # check flag
@table[r].slice!(base...b_line) # fall down
@table[r].concat(Array.new(b_line-base)) # fill e...
# update block pos
(base...@line_s).each do |l|
next unless @table[r][l]
@table[r][l].row = r
@table[r][l].line = l
end
break
end
end
end
}}
** ここまでをテスト [#y200f967]
ここまで正常に動作するかテストコードを実行してみよう!
$ ruby field.rb
さりげなく文字列から一気にフィールドにブロックを配置でき...
#sh(ruby){{
if __FILE__ == "field.rb"
field = Field.new(6,12)
tstr =<<EOF
......
b.....
y.....
b.....
b.r...
r.b...
r....y
bbb.yg
rygrbg
rbygrb
bygrbg
bygrbg
EOF
field.set_table(tstr)
field.print_field
loop do
fallen = field.falldown
field.print_field if fallen
eliminated = field.eliminate
field.print_field if eliminated
break if !fallen && !eliminated
end
}}
落下もしくは消去が起きなくなるまでループが続く.
これを実行すると以下の出力が得られる.
長いな.
--------
| |
|b |
|y |
|b |
|b r |
|r b |
|r y|
|bbb yg|
|rygrbg|
|rbygrb|
|bygrbg|
|bygrbg|
--------
--------
| |
|b |
|y |
|b |
|b |
|r r |
|r b y|
|bbb yg|
|rygrbg|
|rbygrb|
|bygrbg|
|bygrbg|
--------
--------
| |
|b |
|y |
|b |
|b |
|r r |
|r y|
| yg|
|rygrbg|
|rbygrb|
|bygrbg|
|bygrbg|
--------
--------
| |
| |
|b |
|y |
|b |
|b |
|r y|
|r r yg|
|rygrbg|
|rbygrb|
|bygrbg|
|bygrbg|
--------
--------
| |
| |
|b |
|y |
|b |
|b |
| y|
| r yg|
| ygrbg|
| bygrb|
|bygrbg|
|bygrbg|
--------
--------
| |
| |
| |
| |
| |
| |
|b y|
|y r yg|
|bygrbg|
|bbygrb|
|bygrbg|
|bygrbg|
--------
--------
| |
| |
| |
| |
| |
| |
|b y|
|y r yg|
| ygrbg|
| ygrb|
| ygrbg|
| ygrbg|
--------
--------
| |
| |
| |
| |
| |
| |
| y|
| r yg|
| grbg|
| yygrb|
|bygrbg|
|yygrbg|
--------
--------
| |
| |
| |
| |
| |
| |
| y|
| r yg|
| grbg|
| grb|
|b grbg|
| grbg|
--------
--------
| |
| |
| |
| |
| |
| |
| y|
| yg|
| rrbg|
| ggrb|
| grbg|
|b grbg|
--------
--------
| |
| |
| |
| |
| |
| |
| y|
| yg|
| rrbg|
| rb|
| rbg|
|b rbg|
--------
--------
| |
| |
| |
| |
| |
| |
| y|
| yg|
| bg|
| rrb|
| rbg|
|b rrbg|
--------
--------
| |
| |
| |
| |
| |
| |
| y|
| yg|
| bg|
| b|
| bg|
|b bg|
--------
--------
| |
| |
| |
| |
| |
| |
| y|
| g|
| yg|
| bb|
| bg|
|b bg|
--------
--------
| |
| |
| |
| |
| |
| |
| y|
| g|
| yg|
| |
| g|
|b g|
--------
--------
| |
| |
| |
| |
| |
| |
| |
| y|
| g|
| g|
| g|
|b yg|
--------
--------
| |
| |
| |
| |
| |
| |
| |
| y|
| |
| |
| |
|b y |
--------
--------
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|b yy|
--------
ページ名: