#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; }