root/games/robots/auto.c
/*      $NetBSD: auto.c,v 1.11 2009/07/20 06:39:06 dholland Exp $       */

/*-
 * Copyright (c) 1999 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Christos Zoulas.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 *      Automatic move.
 *      intelligent ?
 *      Algo :
 *              IF scrapheaps don't exist THEN
 *                      IF not in danger THEN
 *                              stay at current position
 *                      ELSE
 *                              move away from the closest robot
 *                      FI
 *              ELSE
 *                      find closest heap
 *                      find closest robot
 *                      IF scrapheap is adjacent THEN
 *                              move behind the scrapheap
 *                      ELSE
 *                              take the move that takes you away from the
 *                              robots and closest to the heap
 *                      FI
 *              FI
 */
#include <curses.h>
#include <string.h>
#include "robots.h"

#define ABS(a) (((a)>0)?(a):-(a))
#define MIN(a,b) (((a)>(b))?(b):(a))
#define MAX(a,b) (((a)<(b))?(b):(a))

#define CONSDEBUG(a)

static int distance(int, int, int, int);
static int xinc(int);
static int yinc(int);
static const char *find_moves(void);
static COORD *closest_robot(int *);
static COORD *closest_heap(int *);
static char move_towards(int, int);
static char move_away(COORD *);
static char move_between(COORD *, COORD *);
static int between(COORD *, COORD *);

/* distance():
 * return "move" number distance of the two coordinates
 */
static int
distance(int x1, int y1, int x2, int y2)
{
        return MAX(ABS(ABS(x1) - ABS(x2)), ABS(ABS(y1) - ABS(y2)));
}

/* xinc():
 * Return x coordinate moves
 */
static int
xinc(int dir)
{
        switch(dir) {
        case 'b':
        case 'h':
        case 'y':
                return -1;
        case 'l':
        case 'n':
        case 'u':
                return 1;
        case 'j':
        case 'k':
        default:
                return 0;
        }
}

/* yinc():
 * Return y coordinate moves
 */
static int
yinc(int dir)
{
        switch(dir) {
        case 'k':
        case 'u':
        case 'y':
                return -1;
        case 'b':
        case 'j':
        case 'n':
                return 1;
        case 'h':
        case 'l':
        default:
                return 0;
        }
}

/* find_moves():
 * Find possible moves
 */
static const char *
find_moves(void)
{
        int x, y;
        COORD test;
        const char *m;
        char *a;
        static const char moves[] = ".hjklyubn";
        static char ans[sizeof moves];
        a = ans;

        for (m = moves; *m; m++) {
                test.x = My_pos.x + xinc(*m);
                test.y = My_pos.y + yinc(*m);
                move(test.y, test.x);
                switch(winch(stdscr)) {
                case ' ':
                case PLAYER:
                        for (x = test.x - 1; x <= test.x + 1; x++) {
                                for (y = test.y - 1; y <= test.y + 1; y++) {
                                        move(y, x);
                                        if (winch(stdscr) == ROBOT)
                                                goto bad;
                                }
                        }
                        *a++ = *m;
                }
        bad:;
        }
        *a = 0;
        if (ans[0])
                return ans;
        else
                return "t";
}

/* closest_robot():
 * return the robot closest to us
 * and put in dist its distance
 */
static COORD *
closest_robot(int *dist)
{
        COORD *rob, *end, *minrob = NULL;
        int tdist, mindist;

        mindist = 1000000;
        end = &Robots[MAXROBOTS];
        for (rob = Robots; rob < end; rob++) {
                tdist = distance(My_pos.x, My_pos.y, rob->x, rob->y);
                if (tdist < mindist) {
                        minrob = rob;
                        mindist = tdist;
                }
        }
        *dist = mindist;
        return minrob;
}

/* closest_heap():
 * return the heap closest to us
 * and put in dist its distance
 */
static COORD *
closest_heap(int *dist)
{
        COORD *hp, *end, *minhp = NULL;
        int mindist, tdist;

        mindist = 1000000;
        end = &Scrap[MAXROBOTS];
        for (hp = Scrap; hp < end; hp++) {
                if (hp->x == 0 && hp->y == 0)
                        break;
                tdist = distance(My_pos.x, My_pos.y, hp->x, hp->y);
                if (tdist < mindist) {
                        minhp = hp;
                        mindist = tdist;
                }
        }
        *dist = mindist;
        return minhp;
}

/* move_towards():
 * move as close to the given direction as possible
 */
static char
move_towards(int dx, int dy)
{
        char ok_moves[10], best_move;
        char *ptr;
        int move_judge, cur_judge, mvx, mvy;

        (void)strcpy(ok_moves, find_moves());
        best_move = ok_moves[0];
        if (best_move != 't') {
                mvx = xinc(best_move);
                mvy = yinc(best_move);
                move_judge = ABS(mvx - dx) + ABS(mvy - dy);
                for (ptr = &ok_moves[1]; *ptr != '\0'; ptr++) {
                        mvx = xinc(*ptr);
                        mvy = yinc(*ptr);
                        cur_judge = ABS(mvx - dx) + ABS(mvy - dy);
                        if (cur_judge < move_judge) {
                                move_judge = cur_judge;
                                best_move = *ptr;
                        }
                }
        }
        return best_move;
}

/* move_away():
 * move away form the robot given
 */
static char
move_away(COORD *rob)
{
        int dx, dy;

        dx = sign(My_pos.x - rob->x);
        dy = sign(My_pos.y - rob->y);
        return  move_towards(dx, dy);
}


/* move_between():
 * move the closest heap between us and the closest robot
 */
static char
move_between(COORD *rob, COORD *hp)
{
        int dx, dy;
        float slope, cons;

        /* equation of the line between us and the closest robot */
        if (My_pos.x == rob->x) {
                /*
                 * me and the robot are aligned in x
                 * change my x so I get closer to the heap
                 * and my y far from the robot
                 */
                dx = - sign(My_pos.x - hp->x);
                dy = sign(My_pos.y - rob->y);
                CONSDEBUG(("aligned in x"));
        }
        else if (My_pos.y == rob->y) {
                /*
                 * me and the robot are aligned in y
                 * change my y so I get closer to the heap
                 * and my x far from the robot
                 */
                dx = sign(My_pos.x - rob->x);
                dy = -sign(My_pos.y - hp->y);
                CONSDEBUG(("aligned in y"));
        }
        else {
                CONSDEBUG(("no aligned"));
                slope = (My_pos.y - rob->y) / (My_pos.x - rob->x);
                cons = slope * rob->y;
                if (ABS(My_pos.x - rob->x) > ABS(My_pos.y - rob->y)) {
                        /*
                         * we are closest to the robot in x
                         * move away from the robot in x and
                         * close to the scrap in y
                         */
                        dx = sign(My_pos.x - rob->x);
                        dy = sign(((slope * ((float) hp->x)) + cons) -
                                  ((float) hp->y));
                }
                else {
                        dx = sign(((slope * ((float) hp->x)) + cons) -
                                  ((float) hp->y));
                        dy = sign(My_pos.y - rob->y);
                }
        }
        CONSDEBUG(("me (%d,%d) robot(%d,%d) heap(%d,%d) delta(%d,%d)",
                My_pos.x, My_pos.y, rob->x, rob->y, hp->x, hp->y, dx, dy));
        return move_towards(dx, dy);
}

/* between():
 * Return true if the heap is between us and the robot
 */
int
between(COORD *rob, COORD *hp)
{
        /* I = @ */
        if (hp->x > rob->x && My_pos.x < rob->x)
                return 0;
        /* @ = I */
        if (hp->x < rob->x && My_pos.x > rob->x)
                return 0;
        /* @ */
        /* = */
        /* I */
        if (hp->y < rob->y && My_pos.y > rob->y)
                return 0;
        /* I */
        /* = */
        /* @ */
        if (hp->y > rob->y && My_pos.y < rob->y)
                return 0;
        return 1;
}

/* automove():
 * find and do the best move if flag
 * else get the first move;
 */
char
automove(void)
{
#if 0
        return  find_moves()[0];
#else
        COORD *robot_close;
        COORD *heap_close;
        int robot_dist, robot_heap, heap_dist;

        robot_close = closest_robot(&robot_dist);
        if (robot_dist > 1)
                return('.');
        if (!Num_scrap)
                /* no scrap heaps just run away */
                return move_away(robot_close);

        heap_close = closest_heap(&heap_dist);
        robot_heap = distance(robot_close->x, robot_close->y,
            heap_close->x, heap_close->y);
        if (robot_heap <= heap_dist && !between(robot_close, heap_close)) {
                /*
                 * robot is closest to us from the heap. Run away!
                 */
                return  move_away(robot_close);
        }

        return move_between(robot_close, heap_close);
#endif
}