与えられたぷよぷよフィールドが19連鎖であることを証明する

とあるHP*1にて発見した問題。
久しぶりにCで書いたらSEGVで落ちまくり結局1時間弱かかってしまった。。。

問題

ゲーム「ぷよぷよ」で、フィールドの状態がテキストで与えられたとき、消える「ぷよ」を消して次のフィールドの状態を出力するプログラムを書け。
 たとえば、色をG/Y/Rで表すとき(Green/Yellow/Red)、

GGR
YGG

であればGが消えて

Y R

になります。

また、このプログラムを使って次のフィールドを与えると19連鎖ののちすべてのぷよが消えることを確認し、消える途中の様子をあわせて提出すること。

  GYRR 
RYYGYG 
GYGYRR 
RYGYRG 
YGYRYG 
GYRYRG 
YGYRYR 
YGYRYR 
YRRGRG 
RYGYGG 
GRYGYR 
GRYGYR 
GRYGYR 

※1行目の"GYRR"の前には半角スペースが2つ入っている

バージョン1(思いつくがままに書いたバージョン)
塗りつぶしアルゴリズム(mark関数)が糞

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char **input(int *w, int *h);
void free_board(char **board, int h);
void show(char **board, int w, int h);
int mark(char **board, int w, int h);
void del(char **board, int w, int h);

char **input(int *w, int *h) {
	int ww =0, hh = 0, alloc_h = 10;
	char *p, **board = (char **)malloc(sizeof (char *) * alloc_h);
	char buf[256];
	while (fgets(buf, 256, stdin) != NULL) {
		while (buf[strlen(buf) - 1] == '\n' || buf[strlen(buf) - 1] == '\r') buf[strlen(buf) - 1] = '\0';
		if (ww == 0) {
			ww = strlen(buf);
		} else if (ww != strlen(buf)) {
			free_board(board, hh);
			return NULL;
		}
		board[hh] = strcpy((char *)malloc(ww + 1), buf);
		hh++;
		if (hh >= alloc_h) {
			alloc_h += 10;
			board = (char **)realloc(board, sizeof (char *) * alloc_h);
		}
	}
	*w = ww;
	*h = hh;
	return board;
}

void free_board(char **board, int h) {
	int i;
	for (i = 0; i < h; i++) {
		free(board[i]);
	}
	free(board);
}

void show(char **board, int w, int h) {
	int i;
	for (i = 0; i < w + 2; i++) putchar('-');
	printf("\n");
	for (i = 0; i < h; i++) {
		printf("|%s|\n", board[i]);
	}
	for (i = 0; i < w + 2; i++) putchar('-');
	printf("\n");
}

int mark(char **board, int w, int h) {
	int i, j, k, l, dels, count, total = 0;
	char c;

	for (i = 0; i < h; i++) {
		for (j = 0; j < w; j++) {
			c = board[i][j];
			if (c == ' ' || c == '*') continue;
			board[i][j] = '%';
			dels = 1;
			do {
				count = 0;
				for (k = 0; k < h; k++) {
					for (l = 0; l < w; l++) {
						if (board[k][l] != c) continue;
						if (
							(k - 1 >= 0 && board[k - 1][l] == '%') ||
							(k + 1 < h && board[k + 1][l] == '%') ||
							(l - 1 >= 0 && board[k][l - 1] == '%') ||
							(l + 1 < w && board[k][l + 1] == '%')
						) {
							board[k][l] = '%';
							count++;
						}
					}
				}
				dels += count;
			} while (count > 0);
			if (dels >= 4) {
				c = '*';
				total += dels;
			}
			for (k = 0; k < h; k++) {
				for (l = 0; l < w; l++) {
					if (board[k][l] == '%') board[k][l] = c;
				}
			}
		}
	}
	return total;
}

void del(char **board, int w, int h) {
	int i, j, k, l;
	for (i = h - 1; i >= 0; i--) {
		for (j = 0; j < w; j++) {
			if (board[i][j] == '*') board[i][j] = ' ';
			if (board[i][j] == ' ') {
				for (k = i - 1; k >= 0; k--) {
					if (board[k][j] != ' ' && board[k][j] != '*') {
						board[i][j] = board[k][j];
						if (board[i][j] == '*') board[i][j] = ' ';
						board[k][j] = ' ';
						break;
					}
				}
			}
		}
	}
}

int main(int argc, char **argv) {
	int i = 0, w, h;
	char **board = input(&w, &h);

	if (board == NULL) {
		exit(1);
	}
	show(board, w, h);
	while (mark(board, w, h) > 0) {
		printf("%d:\n", ++i);
		show(board, w, h);
		del(board, w, h);
		show(board, w, h);
	}
	free_board(board, h);
	return 0;
}

塗りつぶしアルゴリズムをスタックを使ったバージョンにしたもの。5重ループが3重ループになりすっきり。108連鎖*2を与えた場合には10倍程度早くなった。

int mark(char **board, int w, int h) {
	int i, j, k, dels, total = 0, x, y;
	int *stack = (int *)malloc(sizeof (int) * w * h), sp;
	char c;

	for (i = 0; i < h; i++) {
		for (j = 0; j < w; j++) {
			c = board[i][j];
			if (c == ' ' || c == '*') continue;
			dels = sp = 0;
			stack[dels++] = i * w + j;
			while (sp < dels) {
				x = stack[sp] % w;
				y = stack[sp] / w;
				board[y][x] = '%';
				if (x - 1 >= 0 && board[y][x - 1] == c) stack[dels++] = y * w + x - 1;
				if (x + 1 < w && board[y][x + 1] == c) stack[dels++] = y * w + x + 1;
				if (y - 1 >= 0 && board[y - 1][x] == c) stack[dels++] = (y - 1) * w + x;
				if (y + 1 < h && board[y + 1][x] == c) stack[dels++] = (y + 1) * w + x;
				sp++;
			}
			if (dels >= 4) {
				c = '*';
				total += dels;
			}
			for (k = 0; k < dels; k++) {
				board[stack[k] / w][stack[k] % w] = c;
			}
		}
	}
	free(stack);
	return total;
}

最初からこれを書けなくなっているのは衰えている証拠かな。。