制作日記

こういうの作った方が、逃げにくいじゃない

パズドラ

あれのパズルのアルゴリズムどうなってるんだろうなーって考えた

単純に考えれば、縦横3個並んでる奴探してピックアップ
ピックアップした中からさらに、ぷよぷよ形式で調べて、コンボ数計算

それじゃ面白くないから、縦横3個並んでる奴を探しつつコンボ数も一度に計算するように作ってみた
続きにソースコードを貼り付けとく
設計とか考えてないし、本当はリファクタリングしないとだけど、とりあえずは出来たのでおk
そうステップ数は200ちょい
デバッグコードとか画面表示用の処理で100ステップ近くあると思うから、
実質150ステップぐらい?

あとは、消した後に上から玉を詰めて再度消去判定する処理入れて
タッチとかドラッグ操作で玉を入れ変えられるようにしたら、ほぼパズドラのアレ
そこらへんはwindowsアプリで作る気が起きないからパス

以下、パズドラ一ヶ月遊んだ感想

人気あるみたいなんで、1ヶ月ほど遊んでみたけど、割と面白い
PTレベルが50台で"聖者の墓 -深層-"クリアまではやった

パズル自体の出来は良い
パズルのルールはコラムスとぷよぷよの合成なんだけど、
全体攻撃か単体攻撃かで、1コンボで消す数を調整する必要があったりで、
RPG要素も地味に上手くパズルに組み込まれてる
パズルのレスポンスも悪くないし

ただし、パズル以外、攻撃時のダメージ表示とか攻撃エフェクトは長くてウザい
6コンボぐらいから長くてイラッとする

それと、ソーシャルゲーだけど、煩わしい他人との交流がない
一応フレンドとかのお助けキャラがあるが、フレンド登録申請送信ボタン押すだけ
承認側も承認ボタン押すだけで、メッセージのやり取りなんてない

さらには、フレンド要素が良い感じに救済措置になってる

課金誘導のタイミングは下手とは思わないけど、上手くもない
スタミナ回復とかコンティニューのために払いたくなるかっていうと……

ただ、ユーザ数が多く、手軽に遊べるおかげで、課金に誘われるタイミングが多い
なんだかんだで課金はかなりの額になる

途中からはリーダースキルとかPT構成重視になってくるのがしんどい
大体は攻撃力高倍率PTにするか、根性で耐えるか、ってパターン

そういうPT組まないと、平均5コンボぐらい出しても、無理なものは無理

そのくせ、育成がひたすらにメンドクサイ

一応スタミナ消費なしでも遊べるけど、達成感皆無


#include
#include
#include

#define DBUG

#define WIDTH 6
#define HEIGHT 5
#define for_each_ball(func) \
do{ \
int i, j; \
for (i = 0; i < HEIGHT; i++) \
for (j = 0; j < WIDTH; j++) \
func(i,j); \
}while(0)

enum color
{
RED,
BLUE,
YELLOW,
BLACK,
WHITE,
LAST_COLOR
};

struct ball
{
char color;
char is_erase;
char is_checked;
};

int combo = 0;
#ifdef DEBUG
int skip = 0;
#endif /* DEBUG */

struct ball balls[HEIGHT][WIDTH] = {{{0,0,0}}};
struct dir
{
int y;
int x;
};

struct dir dir_table[] = {{0,1},{1,0}};

#define for_each_dir_table(height, width, func) \
do{ \
int i, max = (int)(sizeof(dir_table)/sizeof(dir_table[0])); \
for (i = 0; i < max; i++) \
func(height, width, dir_table[i].y,dir_table[i].x); \
}while(0)

struct dir all_dir_table[] = {{0,1},{1,0},{0,-1},{-1,0}};
static void for_all_dir_same_color_only(int height, int width, char color, void (*func)(int, int))
{
int i, max = (int)(sizeof(all_dir_table)/sizeof(all_dir_table[0]));
for (i = 0; i < max; i++)
{
if ( height + all_dir_table[i].y < HEIGHT && height + all_dir_table[i].y >= 0
&& width + all_dir_table[i].x < WIDTH && width + all_dir_table[i].x >= 0
&& balls[(height + all_dir_table[i].y)][(width + all_dir_table[i].x)].color == color)
{
#ifdef DEBUG
printf("search h:%d, w:%d\n", height + all_dir_table[i].y, width + all_dir_table[i].x);
#endif /* DEBUG */
func(height + all_dir_table[i].y, width + all_dir_table[i].x);
}
}
}
static void search_same_color_ball(int height, int width);

static void init_ball_search_flag(int height, int width)
{
balls[height][width].is_checked = 0;
}

static void init_balls_search_flag(void)
{
for_each_ball(init_ball_search_flag);
}
static void init_ball(int height, int width)
{
balls[height][width].color = rand() * 1.0 / RAND_MAX * (LAST_COLOR - 1);
}
static void init(void)
{
srand((unsigned int)time(NULL));

for_each_ball(init_ball);
}

static void print_ball(int height, int width)
{
if (!height && !width)
{
printf(" 0 1 2 3 4 5\n");
}
if (!width)
{
printf("%d ", height);
}
char ball_color;
switch (balls[height][width].color)
{
case RED:
ball_color = 'R';
break;
case BLUE:
ball_color = 'B';
break;
case YELLOW:
ball_color = 'Y';
break;
case BLACK:
ball_color = 'K';
break;
case WHITE:
ball_color = 'W';
break;
default:
ball_color = ' ';
}
printf(" %c ", ball_color);
if (width == WIDTH - 1)
{
printf("\n");
}
}

static void print_screen(void)
{
// system("cls");
for_each_ball(print_ball);
}

static void print_erase_info(int height, int width)
{
char erase_info;
switch (balls[height][width].is_erase)
{
case 0:
erase_info = 'o';
break;
case 1:
erase_info = 'x';
break;
default:
erase_info = ' ';
}
printf(" %c ", erase_info);
if (width == WIDTH - 1)
{
printf("\n");
}
}

static void print_erase_infos(void)
{
for_each_ball(print_erase_info);
}
static int count_line(int height, int width, int y, int x)
{
int tmp_height, tmp_width;
int same_color_num;
char current_color = balls[height][width].color;

tmp_height = height + y;
tmp_width = width + x;
for (same_color_num = 1; ; same_color_num++,tmp_height += y, tmp_width += x)
{
if( (balls[tmp_height][tmp_width].color != current_color)
|| (tmp_height >= HEIGHT || tmp_height < 0)
|| (tmp_width >= WIDTH) || tmp_width < 0)
{
break;
}
}

tmp_height = height - y;
tmp_width = width - x;
for ( ; ; same_color_num++,tmp_height -= y, tmp_width -= x)
{
if( (balls[tmp_height][tmp_width].color != current_color)
|| (tmp_height >= HEIGHT || tmp_height < 0)
|| (tmp_width >= WIDTH) || tmp_width < 0)
{
break;
}
}
return same_color_num;
}

static int search_line(int height, int width, int y, int x)
{
int same_color_num;

if (balls[height][width].is_checked > 1 || (same_color_num = count_line(height, width, y, x)) < 3) return same_color_num;
balls[height][width].is_erase = 1;
#ifdef DEBUG
printf("erase : h:%d, w:%d\n", height, width);
#endif /* DEBUG */

for_all_dir_same_color_only(height, width, balls[height][width].color, search_same_color_ball);
return same_color_num;
}

static void search_same_color_ball(int height, int width)
{

if ( (height >= HEIGHT || height < 0)
|| (width >= WIDTH || width < 0)
|| balls[height][width].is_erase
|| balls[height][width].is_checked)
return;

balls[height][width].is_checked++;
for_each_dir_table(height, width, search_line);
}

static void search_ball(int height, int width)
{
if (balls[height][width].is_erase)
{
#ifdef DEBUG
printf("skip : h:%d, w:%d\n", height, width);
skip++;
#endif /* DEBUG */
return;
}
search_same_color_ball(height, width);
if (balls[height][width].is_erase)
{
#ifdef DEBUG
printf("combo : h:%d, w:%d\n", height, width);
#endif /* DEBUG */
combo++;
}
init_balls_search_flag();
}

static void search_erase_ball()
{
for_each_ball(search_ball);
}

int main(void)
{
init();
print_screen();
search_erase_ball();
print_erase_infos();
#ifdef DEBUG
printf("combo:%d, skip:%d", combo, skip);
#else
printf("combo:%d", combo);
#endif /* DEBUG */
return 0;
}

コメントの投稿

非公開コメント

プロフィール

Author:drab
霊夢改変キャラ
「12 3 ! {V} [_]」
公開中
L霊夢でもl_reimuでも好きなように読んでください

最新記事
最新コメント
月別アーカイブ
カテゴリ
検索フォーム
RSSリンクの表示
リンク