Я работаю над игрой, похожей на тетрис, на языке C, и у меня есть две функции: check_left_side_occupation
и check_right_side_occupation
, которые очень похожи. Я хочу провести их рефакторинг, чтобы избежать дублирования кода, но не знаю, как этого добиться. Вот соответствующая часть моего кода:
void check_right_side_occupation(int (*field)[field_width], figure *piece)
{
int x, y;
for (y = 0; y < piece_size; y++) {
/* first different part */
for (x = piece_size - 1; x >= 0; x--) {
/* end */
if (piece->form[y][x] == 0)
continue;
else {
if (field[y + piece->y_decline][x + piece->x_shift] == 1) {
/* second different part */
piece->x_shift--;
/* end */
return;
}
}
}
}
}
void check_left_side_occupation(int (*field)[field_width], figure *piece)
{
int x, y;
for (y = 0; y < piece_size; y++) {
/* first different part */
for (x = 0; x < piece_size; x++) {
/* end */
if (piece->form[y][x] == 0)
continue;
else {
if (field[y + piece->y_decline][x + piece->x_shift] == 1) {
/* second different part */
piece->x_shift++;
/* end */
return;
}
}
}
}
}
void side_pixel_occupied_by_field(
move_direction direction, int (*field)[field_width], figure *piece
)
{
switch (direction) {
case left:
check_left_side_occupation(field, piece);
break;
case right:
check_right_side_occupation(field, piece);
}
}
Есть ли способ провести рефакторинг этих функций, чтобы избежать дублирования кода и при этом сохранить функциональность проверки как левой, так и правой части фрагмента?
Любая помощь или предложения будут очень признательны!
Вы можете использовать параметр направления в функции side_pixel_occupied_by_field
. Эта версия требует, чтобы move_direction
был определен как:
typedef enum { left = 1, right = -1 } move_direction;
void side_pixel_occupied_by_field(move_direction direction,
int (*field)[field_width], figure *piece) {
int start, end;
// set start and end depending on the direction:
if (direction == right) {
start = piece_size - 1;
end = -1;
} else {
start = 0;
end = piece_size;
}
for (int y = 0; y < piece_size; y++) {
// loop in the wanted direction:
for (int x = start; x != end; x += direction) {
if (piece->form[y][x] != 0 &&
field[y + piece->y_decline][x + piece->x_shift] == 1)
{
piece->x_shift += direction; // use direction here too
return;
}
}
}
}
Альтернативно, без необходимости менять текущее определение move_direction
:
typedef struct {
int start;
int end;
int increment;
} start_end_inc;
void side_pixel_occupied_by_field(move_direction direction,
int (*field)[field_width], figure *piece) {
// static if piece_size doesn't change:
const start_end_inc sei[] = {{0, piece_size, 1},
{piece_size - 1, -1, -1}};
const start_end_inc *seip = &sei[direction == right];
for (int y = 0; y < piece_size; y++) {
for (int x = seip->start; x != seip->end; x += seip->increment) {
if (piece->form[y][x] != 0 &&
field[y + piece->y_decline][x + piece->x_shift] == 1)
{
piece->x_shift += seip->increment;
return;
}
}
}
}
@chqrlie Я оставлю это на усмотрение ОП, чтобы он исправил, если использование предложенных мной значений невозможно. :)
Боюсь, полагаться на значения перечисления менее читабельно и более хрупко. Я бы не рекомендовал это делать, особенно новичкам.
@chqrlie Возможно, ты прав. Я добавил альтернативу, которая не зависит от значений move_direction
.
Конечно, вы можете факторизовать код и использовать одну и ту же функцию в обоих случаях:
void check_side_occupation(int (*field)[field_width],
figure *piece,
int start_x, int end_x, int incr_x)
{
for (int y = 0; y < piece_size; y++) {
/* first different part */
for (int x = start_x; x != end_x; x += incr_x) {
/* end */
if (piece->form[y][x] == 0)
continue;
if (field[y + piece->y_decline][x + piece->x_shift] == 1) {
/* second different part */
piece->x_shift += incr_x;
/* end */
return;
}
}
}
}
void side_pixel_occupied_by_field(move_direction direction,
int (*field)[field_width],
figure *piece)
{
switch (direction) {
case left:
check_side_occupation(field, piece, 0, piece_size, 1);
break;
case right:
check_side_occupation(field, piece, piece_size - 1, -1, -1);
break;
}
}
Чтобы избежать ограничений на числовые значения перечисляемых констант, вам следует использовать дополнительную переменную
incr
.