206 lines
5.4 KiB
C
206 lines
5.4 KiB
C
#include "pch.c"
|
|
#pragma hdrstop
|
|
|
|
static int left_turn[SOL_DIRS] =
|
|
{
|
|
SOL_DIR_DOWN, SOL_DIR_LEFT, SOL_DIR_UP, SOL_DIR_RIGHT
|
|
};
|
|
|
|
static int right_turn[SOL_DIRS] =
|
|
{
|
|
SOL_DIR_UP, SOL_DIR_RIGHT, SOL_DIR_DOWN, SOL_DIR_LEFT
|
|
};
|
|
|
|
static BYTE dir_wall[SOL_DIRS] =
|
|
{
|
|
MAZE_WALL_LEFT, MAZE_WALL_UP, MAZE_WALL_RIGHT, MAZE_WALL_DOWN
|
|
};
|
|
|
|
static int dir_cloff[SOL_DIRS][2] =
|
|
{
|
|
-1,0, 0,-1, 1,0, 0,1
|
|
};
|
|
|
|
static FaAngle dir_ang[SOL_DIRS];
|
|
static FxPt2 dir_off[SOL_DIRS];
|
|
|
|
/* We want to traverse one quarter of a circle in the given number of
|
|
steps. The distance is the arc length which is r*pi/2. Divide that
|
|
by the number of steps to get the distance each step should travel */
|
|
#define ARC_STEP 5
|
|
#define ARC_STEPS (90/ARC_STEP)
|
|
|
|
#define REVERSE_STEP (2*ARC_STEP)
|
|
#define REVERSE_STEPS (180/REVERSE_STEP)
|
|
|
|
static void SetView(MazeSolution *sol, MazeView *vw)
|
|
{
|
|
vw->ang = dir_ang[sol->dir];
|
|
vw->pos.x = CellToMfx(sol->clx)+dir_off[sol->dir].x;
|
|
vw->pos.y = CellToMfx(sol->cly)+dir_off[sol->dir].y;
|
|
}
|
|
|
|
void SolveMazeSetGoals(MazeSolution *sol, MazeGoal *goals, int ngoals)
|
|
{
|
|
sol->goals = goals;
|
|
sol->ngoals = ngoals;
|
|
}
|
|
|
|
void SolveMazeStart(MazeView *vw,
|
|
Cell *maze, int w, int h,
|
|
IntPt2 *start, int start_dir,
|
|
MazeGoal *goals, int ngoals,
|
|
int turn_to,
|
|
MazeSolution *sol)
|
|
{
|
|
dir_ang[SOL_DIR_LEFT] = FaDeg(180);
|
|
dir_ang[SOL_DIR_UP] = FaDeg(90);
|
|
dir_ang[SOL_DIR_RIGHT] = FaDeg(0);
|
|
dir_ang[SOL_DIR_DOWN] = FaDeg(270);
|
|
|
|
dir_off[SOL_DIR_LEFT].x = CellToMfx(1)-FX_MIN_VALUE;
|
|
dir_off[SOL_DIR_LEFT].y = CellToMfx(1)/2;
|
|
dir_off[SOL_DIR_UP].x = CellToMfx(1)/2;
|
|
dir_off[SOL_DIR_UP].y = CellToMfx(1)-FX_MIN_VALUE;
|
|
dir_off[SOL_DIR_RIGHT].x = FxVal(0);
|
|
dir_off[SOL_DIR_RIGHT].y = CellToMfx(1)/2;
|
|
dir_off[SOL_DIR_DOWN].x = CellToMfx(1)/2;
|
|
dir_off[SOL_DIR_DOWN].y = FxVal(0);
|
|
|
|
sol->clx = start->x;
|
|
sol->cly = start->y;
|
|
sol->dir = start_dir;
|
|
sol->maze = maze;
|
|
sol->w = w;
|
|
sol->h = h;
|
|
sol->ani_state = ANI_STATE_NONE;
|
|
|
|
switch(turn_to)
|
|
{
|
|
case SOL_TURN_RIGHT:
|
|
sol->turn_to = left_turn;
|
|
sol->turn_away = right_turn;
|
|
sol->dir_sign = 1;
|
|
break;
|
|
|
|
case SOL_TURN_LEFT:
|
|
sol->turn_to = right_turn;
|
|
sol->turn_away = left_turn;
|
|
sol->dir_sign = -1;
|
|
break;
|
|
}
|
|
|
|
SolveMazeSetGoals(sol, goals, ngoals);
|
|
|
|
SetView(sol, vw);
|
|
}
|
|
|
|
#define MazeAt(x, y) (sol->maze+(x)+(y)*(sol->w))
|
|
|
|
MazeGoal *SolveMazeStep(MazeView *vw, MazeSolution *sol)
|
|
{
|
|
Cell *cell;
|
|
int i, dir, turn_to;
|
|
|
|
if (sol->ani_state != ANI_STATE_NONE)
|
|
{
|
|
if (--sol->ani_count == 0)
|
|
{
|
|
sol->ani_state = ANI_STATE_NONE;
|
|
SetView(sol, vw);
|
|
}
|
|
}
|
|
|
|
switch(sol->ani_state)
|
|
{
|
|
case ANI_STATE_TURN_TO:
|
|
vw->pos.x += FxMulDiv(FaCos(vw->ang),
|
|
FxFltVal(PI*MAZE_CELL_SIZE/2),
|
|
FxVal(ARC_STEPS*2));
|
|
vw->pos.y += FxMulDiv(FaSin(vw->ang),
|
|
FxFltVal(PI*MAZE_CELL_SIZE/2),
|
|
FxVal(ARC_STEPS*2));
|
|
vw->ang = FaAdd(vw->ang, sol->dir_sign*FaDeg(ARC_STEP));
|
|
return NULL;
|
|
|
|
case ANI_STATE_TURN_AWAY:
|
|
vw->pos.x += FxMulDiv(FaCos(vw->ang),
|
|
FxFltVal(PI*MAZE_CELL_SIZE/2),
|
|
FxVal(ARC_STEPS*2));
|
|
vw->pos.y += FxMulDiv(FaSin(vw->ang),
|
|
FxFltVal(PI*MAZE_CELL_SIZE/2),
|
|
FxVal(ARC_STEPS*2));
|
|
vw->ang = FaAdd(vw->ang, sol->dir_sign * -FaDeg(ARC_STEP));
|
|
return NULL;
|
|
|
|
case ANI_STATE_FORWARD:
|
|
vw->pos.x += FxMulDiv(FaCos(vw->ang), MAZE_CELL_SIZE, ARC_STEPS);
|
|
vw->pos.y += FxMulDiv(FaSin(vw->ang), MAZE_CELL_SIZE, ARC_STEPS);
|
|
return NULL;
|
|
|
|
case ANI_STATE_REVERSE:
|
|
vw->ang = FaAdd(vw->ang, sol->dir_sign*FaDeg(REVERSE_STEP));
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < sol->ngoals; i++)
|
|
{
|
|
if (sol->clx == sol->goals[i].clx &&
|
|
sol->cly == sol->goals[i].cly)
|
|
{
|
|
return &sol->goals[i];
|
|
}
|
|
}
|
|
|
|
cell = MazeAt(sol->clx, sol->cly);
|
|
|
|
dir = sol->dir;
|
|
for (i = 0; i < SOL_DIRS-1; i++)
|
|
{
|
|
turn_to = sol->turn_to[dir];
|
|
if ((dir_wall[turn_to] & cell->can_see) == 0)
|
|
{
|
|
/* No wall present when turned, so turn that way */
|
|
sol->clx += dir_cloff[turn_to][0];
|
|
sol->cly += dir_cloff[turn_to][1];
|
|
sol->dir = turn_to;
|
|
|
|
sol->ani_count = ARC_STEPS;
|
|
switch(i)
|
|
{
|
|
case 0:
|
|
sol->ani_state = ANI_STATE_TURN_TO;
|
|
break;
|
|
|
|
case 1:
|
|
sol->ani_state = ANI_STATE_FORWARD;
|
|
break;
|
|
|
|
case 2:
|
|
sol->ani_state = ANI_STATE_TURN_AWAY;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
/* Wall present, turn away and check again */
|
|
dir = sol->turn_away[dir];
|
|
}
|
|
}
|
|
|
|
if (i == SOL_DIRS-1)
|
|
{
|
|
/* Dead end. Turn around */
|
|
dir = sol->turn_to[sol->turn_to[sol->dir]];
|
|
sol->clx += dir_cloff[dir][0];
|
|
sol->cly += dir_cloff[dir][1];
|
|
sol->dir = dir;
|
|
|
|
sol->ani_state = ANI_STATE_REVERSE;
|
|
sol->ani_count = REVERSE_STEPS;
|
|
}
|
|
|
|
return NULL;
|
|
}
|