/*--------------------------------------------------------------------------*/ /* computer */ /*--------------------------------------------------------------------------*/ #include #include #include #include #include #include #include "computer.h" #include "actors.h" #include "board.h" #include "field.h" #include "main.h" #include "list.h" /*--------------------------------------------------------------------------*/ /* defines */ /*--------------------------------------------------------------------------*/ #define DATA(x) (computer_data[configs[computer_side]->data_num].x) #define FIELD_SKILL DATA(skill_level) #define SHOOT_DISTANCE DATA(shoot_distance) #define ACTOR_DISTANCE DATA(actor_distance) #define ACTOR_RECHARGE DATA(actor_recharge) #define NUM_GOOD_MOVES DATA(num_good_moves) #define FIELD_SKILL_HARD 0 #define FIELD_SKILL_MEDIUM 10 #define FIELD_SKILL_EASY 20 /*--------------------------------------------------------------------------*/ /* structures */ /*--------------------------------------------------------------------------*/ typedef struct { int light_sum; /* damage light caused */ int dark_sum; /* damage dark caused */ int light_hits; /* number of times light hit */ int dark_hits; /* number of times dark hit */ int count; } DAMAGE; typedef struct { /* field */ int skill_level; /* how easy should the computer be */ int shoot_distance; /* escape missiles this close */ int actor_distance; /* escape actors this close, who */ int actor_recharge; /* are at this recharge level */ /* board */ int num_good_moves; /* good moves to consider (max 32) */ } COMPUTER_DATA; typedef struct { int spell; int x1, y1; int x2, y2; int score; } MOVE; /*--------------------------------------------------------------------------*/ /* functions */ /*--------------------------------------------------------------------------*/ static void computer_board(COMMAND *cmd); static void computer_board_teleport_exchange(int spell, MOVE *move, int me, int him); static void computer_board_teleport(MOVE *move); static void computer_board_heal(MOVE *move); static void computer_board_shift_time(MOVE *move); static void computer_board_exchange(MOVE *move); static void computer_board_summon_elemental(MOVE *move); static void computer_board_revive(MOVE *move); static void computer_board_imprison(MOVE *move); static int computer_board_spell(void); static void computer_board_battle_score(MOVE *move); static void computer_board_move_score(MOVE *move); static void computer_board_insert_move(int x1, int y1, int x2, int y2); static int computer_board_good_moves(int pass); static void computer_field(COMMAND *cmd); static int computer_field_offense(COMMAND *cmd); static int computer_field_defense(COMMAND *cmd); static void computer_field_move(COMMAND *cmd, int do_move); static int computer_field_collision_course(FIELD_ACTOR *target, FIELD_ACTOR *source, int dir); static int computer_field_direction(int x1, int y1); static void computer_field_stop(void); extern void Xarchon_AI_Computer(int *x1, int *y1, int *x2, int *y2); /*--------------------------------------------------------------------------*/ /* variables */ /*--------------------------------------------------------------------------*/ static MOVE good_moves[33]; static int computer_side; static COMPUTER_CONFIG *configs[2]; static void (*computer_board_spell_funcs[SPELL_COUNT_2])(MOVE *move) = { NULL, computer_board_teleport, computer_board_heal, computer_board_shift_time, computer_board_exchange, computer_board_summon_elemental, computer_board_revive, computer_board_imprison, NULL, /* cease conjuring */ NULL }; static COMPUTER_DATA computer_data[3] = { { 45, 2, 7, FPS * 0.5, 6 }, { 9, 2, 7, FPS * 0.5, 4 }, { 4, 5, 5, FPS * 0.3, 1 } }; static DAMAGE computer_damage[ACTOR_NUM_LIGHT + 1][ACTOR_NUM_DARK + 1]; static int computer_damage_ok = 0; /*--------------------------------------------------------------------------*/ /* computer_start */ /*--------------------------------------------------------------------------*/ void computer_start(int side, COMPUTER_CONFIG *config) { configs[side] = config; } /*--------------------------------------------------------------------------*/ /* computer_turn */ /*--------------------------------------------------------------------------*/ void computer_turn(int side, int mode, COMMAND *cmd) { computer_side = side; switch (mode) { case IFACE_BOARD: computer_board(cmd); break; case IFACE_FIELD_START: break; case IFACE_FIELD: computer_field(cmd); break; case IFACE_FIELD_STOP: computer_field_stop(); break; } } /*--------------------------------------------------------------------------*/ /* computer_board */ /*--------------------------------------------------------------------------*/ void computer_board(COMMAND *cmd) { int n; MOVE *move; if (computer_board_spell()) move = &good_moves[0]; else { if (configs[computer_side]->old_board_mode) { n = computer_board_good_moves(1); move = &good_moves[random() % n]; } else { move = &good_moves[0]; Xarchon_AI_Computer(&move->x1, &move->y1, &move->x2, &move->y2); } move->spell = 0; } cmd->b.spell = move->spell; cmd->b.x1 = move->x1; cmd->b.y1 = move->y1; cmd->b.x2 = move->x2; cmd->b.y2 = move->y2; } /*--------------------------------------------------------------------------*/ /* computer_board_teleport_exchange */ /*--------------------------------------------------------------------------*/ void computer_board_teleport_exchange(int spell, MOVE *move, int me, int him) { int x1, y1; int x2, y2; int flags; board_find_actor(me, &x1, &y1); flags = CELL_POWER | CELL_IMPRISON; if (x1 != -1 && (board_cells[y1][x1].flags & flags) == 0) { board_find_actor(him, &x2, &y2); if (spell == SPELL_TELEPORT) /* it is possible to teleport */ flags ^= CELL_IMPRISON; /* *into* an imprisoned square */ if (x2 != -1 && (board_cells[y2][x2].flags & flags) == 0) { move->spell = spell; move->x1 = x1; move->y1 = y1; move->x2 = x2; move->y2 = y2; } } } /*--------------------------------------------------------------------------*/ /* computer_board_teleport */ /*--------------------------------------------------------------------------*/ void computer_board_teleport(MOVE *move) { int me, him; int x, y; int lumi; if (random() % 5 == 0) { me = (computer_side == 0) ? ACTOR_UNICORN : ACTOR_BASILISK; him = (computer_side == 0) ? ACTOR_BASILISK : ACTOR_UNICORN; board_find_actor(him, &x, &y); if (x != -1) { lumi = board_cell_lumi(&board_cells[y][x]); if ((computer_side == 0 && (lumi & CELL_LIGHT)) || (computer_side == 1 && (lumi & CELL_DARK))) computer_board_teleport_exchange(SPELL_TELEPORT, move, me, him); } } } /*--------------------------------------------------------------------------*/ /* computer_board_heal */ /*--------------------------------------------------------------------------*/ void computer_board_heal(MOVE *move) { int x, y; int type; type = (computer_side == 0) ? ACTOR_UNICORN : ACTOR_BASILISK; board_find_actor(type, &x, &y); if (x != -1 && (board_cells[y][x].flags & CELL_POWER) == 0 && board_cells[y][x].actor->strength <= actors_list[type].strength / 2) { move->spell = SPELL_HEAL; move->x1 = x; move->y1 = y; } } /*--------------------------------------------------------------------------*/ /* computer_board_shift_time */ /*--------------------------------------------------------------------------*/ void computer_board_shift_time(MOVE *move) { int x, y; if (!board_is_imprison_ok()) { for (y = 0; y < BOARD_YCELLS; y++) for (x = 0; x < BOARD_XCELLS; x++) if (board_cells[y][x].flags & CELL_IMPRISON) { move->spell = SPELL_SHIFT_TIME; return; } } if (!spell_avails[!computer_side][SPELL_SHIFT_TIME]) { move->spell = SPELL_SHIFT_TIME; return; } } /*--------------------------------------------------------------------------*/ /* computer_board_exchange */ /*--------------------------------------------------------------------------*/ void computer_board_exchange(MOVE *move) { int me, him; if (board_turn % 40 == random() % 40) { me = (computer_side == 0) ? ACTOR_DJINNI : ACTOR_DRAGON; him = (computer_side == 0) ? ACTOR_GOBLIN : ACTOR_KNIGHT; computer_board_teleport_exchange(SPELL_EXCHANGE, move, me, him); } } /*--------------------------------------------------------------------------*/ /* computer_board_summon_elemental */ /*--------------------------------------------------------------------------*/ void computer_board_summon_elemental(MOVE *move) { int x, y; int type; if (board_turn % 20 == random() % 20) { type = (computer_side == 0) ? ACTOR_SHAPESHIFTER : ACTOR_PHOENIX; board_find_actor(type, &x, &y); if (x != -1 && (board_cells[y][x].flags & CELL_POWER) == 0) { move->spell = SPELL_SUMMON_ELEMENTAL; move->x1 = x; move->y1 = y; } } } /*--------------------------------------------------------------------------*/ /* computer_board_revive */ /*--------------------------------------------------------------------------*/ void computer_board_revive(MOVE *move) { int actors[10], i; if (random() % 2 == 0) if (board_revive_check(actors, NULL, NULL)) for (i = 0; actors[i] != 0; i++) if (actors[i] == ACTOR_PHOENIX || actors[i] == ACTOR_DJINNI || actors[i] == ACTOR_UNICORN || actors[i] == ACTOR_SHAPESHIFTER || actors[i] == ACTOR_DRAGON || actors[i] == ACTOR_BASILISK) { move->spell = SPELL_REVIVE; move->x1 = actors[i]; return; } } /*--------------------------------------------------------------------------*/ /* computer_board_imprison */ /*--------------------------------------------------------------------------*/ void computer_board_imprison(MOVE *move) { int x, y; int target_x, target_y; int type; int count; if (board_is_imprison_ok()) { type = (computer_side == 0) ? ACTOR_SORCERESS : ACTOR_WIZARD; board_find_actor(type, &target_x, &target_y); if (target_x == -1) { count = 0; for (y = 0; y < BOARD_YCELLS; y++) for (x = 0; x < BOARD_XCELLS; x++) if (board_cells[y][x].actor != NULL && actor_is_side(board_cells[y][x].actor, !computer_side)) { target_x = x; target_y = y; count++; } if (count != 1) target_x = -1; } if (target_x != -1 && (board_cells[target_y][target_x].flags & CELL_POWER) == 0) { move->spell = SPELL_IMPRISON; move->x1 = target_x; move->y1 = target_y; } } } /*--------------------------------------------------------------------------*/ /* computer_board_spell */ /*--------------------------------------------------------------------------*/ int computer_board_spell(void) { int x, y; int type; MOVE move; int i = 0, i0; /* spells are only available if we have non-imprisoned master */ type = (computer_side == 0) ? ACTOR_WIZARD : ACTOR_SORCERESS; if (!board_find_actor(type, &x, &y)) return 0; if (board_cells[y][x].flags & CELL_IMPRISON) return 0; /* select a random spell that is worth casting */ move.spell = 0; i0 = random() % (SPELL_COUNT - 2) + 1; /* returns 1..7 */ for (i = i0 + 1; i != i0; i++) { if (i < SPELL_FIRST) /* no spell number 0 */ continue; if (i >= SPELL_LAST) { i = SPELL_FIRST - 1; continue; } if (spell_avails[computer_side][i]) { computer_board_spell_funcs[i](&move); if (move.spell != 0) { good_moves[0].spell = i; good_moves[0].x1 = move.x1; good_moves[0].y1 = move.y1; good_moves[0].x2 = move.x2; good_moves[0].y2 = move.y2; return 1; } } } return 0; } /*--------------------------------------------------------------------------*/ /* computer_board_battle_score */ /*--------------------------------------------------------------------------*/ void computer_board_battle_score(MOVE *move) { int health; int attacker_damage, defender_damage; int attacker_hits, defender_hits; health = field_initial_life(board_cells[move->y1][move->x1].actor, &board_cells[move->y2][move->x2]); computer_field_score(board_cells[move->y1][move->x1].actor, board_cells[move->y2][move->x2].actor, &attacker_damage, &defender_damage, &attacker_hits, &defender_hits); if (health <= defender_damage) /* our guy is going to die */ move->score = 0; else { health = field_initial_life(board_cells[move->y2][move->x2].actor, &board_cells[move->y2][move->x2]); move->score = attacker_damage * 500 / health; } } /*--------------------------------------------------------------------------*/ /* computer_board_move_score */ /*--------------------------------------------------------------------------*/ void computer_board_move_score(MOVE *move) { int x, y; int n_actors, n_power_points; /* score move regardless of power points */ if (board_cells[move->y2][move->x2].actor == NULL) move->score = random() % 500; else computer_board_battle_score(move); /* if moving from a regular square into a power point, give bonus */ if ((board_cells[move->y1][move->x1].flags & CELL_POWER) == 0 && (board_cells[move->y2][move->x2].flags & CELL_POWER) != 0) { n_actors = 0; n_power_points = 1; for (y = 0; y < BOARD_YCELLS; y++) for (x = 0; x < BOARD_XCELLS; x++) if (board_cells[y][x].actor != NULL && actor_is_side(board_cells[y][x].actor, computer_side)) { n_actors++; if (board_cells[y][x].flags & CELL_POWER) n_power_points++; } if (n_actors >= 5) move->score += n_power_points * 500; } /* if moving from a power point to a regular square, reduce score */ /* or: if moving the master */ if ((board_cells[move->y1][move->x1].flags & CELL_POWER) != 0 || (board_cells[move->y1][move->x1].actor->type & ACTOR_MASTER) == ACTOR_MASTER) move->score /= 10; } /*--------------------------------------------------------------------------*/ /* computer_board_insert_move */ /*--------------------------------------------------------------------------*/ void computer_board_insert_move(int x1, int y1, int x2, int y2) { MOVE move; int i, min_i; move.x1 = x1; move.y1 = y1; move.x2 = x2; move.y2 = y2; computer_board_move_score(&move); for (i = 0; i < NUM_GOOD_MOVES; i++) if (good_moves[i].x1 == -1) break; if (i >= NUM_GOOD_MOVES) { min_i = 0; for (i = 1; i < NUM_GOOD_MOVES; i++) if (good_moves[i].score < good_moves[min_i].score) min_i = i; if (good_moves[min_i].score < move.score) i = min_i; } if (i < NUM_GOOD_MOVES) memcpy(&good_moves[i], &move, sizeof(MOVE)); } /*--------------------------------------------------------------------------*/ /* computer_board_good_moves */ /*--------------------------------------------------------------------------*/ int computer_board_good_moves(int pass) { int i; int x1, y1; int x2, y2; ACTOR *actor; for (i = 0; i < NUM_GOOD_MOVES + 1; i++) good_moves[i].x1 = -1; for (y1 = 0; y1 < BOARD_YCELLS; y1++) for (x1 = 0; x1 < BOARD_XCELLS; x1++) { if (!board_is_pickable(x1, y1, 0)) continue; /* filtering (pass 1 only): pick only ground actors on */ /* even-numbered turns; only fly ones on odd-numbered turns */ actor = board_cells[y1][x1].actor; /* if ((actor->type & ACTOR_MASK) != ACTOR_MANTICORE) continue; */ if (pass == 1 && (((board_turn / 2) % 2 != 0 && (actor->type & ACTOR_FLY) == 0) || ((board_turn / 2) % 2 == 0 && (actor->type & ACTOR_GROUND) == 0))) continue; /* filtering (pass 1,2 only): don't pick the master */ if (pass == 2 && (actor->type & ACTOR_MASTER) == 0) continue; for (y2 = y1 - actor->distance; y2 <= y1 + actor->distance; y2++) for (x2 = x1 - actor->distance; x2 <= x1 + actor->distance; x2++) if (y2 >= 0 && y2 < BOARD_YCELLS && x2 >= 0 && x2 < BOARD_XCELLS && (x2 != x1 || y2 != y1) && (board_cells[y2][x2].actor == NULL || actor_is_side(board_cells[y2][x2].actor, !computer_side)) && board_get_route(x1, y1, x2, y2) != NULL) computer_board_insert_move(x1, y1, x2, y2); } for (i = 0; i < NUM_GOOD_MOVES && good_moves[i].x1 != -1; i++) ; if (i == 0 && pass < 3) /* retry (the higher the pass num, */ i = computer_board_good_moves(pass + 1); /* the less filters are done) */ if (i == 0) { fprintf(stderr, "computer_board_good_moves(): cannot find any possible move to make\n"); exit(EXIT_FAILURE); } return i; } /*--------------------------------------------------------------------------*/ /* computer_field */ /*--------------------------------------------------------------------------*/ void computer_field(COMMAND *cmd) { int ok = 0; if (field_frame_time % (random() % (FIELD_SKILL / 10 + 1) + 1) != 0) { cmd->f.dir = 0; cmd->f.fire = 0; return; } if ((field_me->actor->type & ACTOR_WEAPON_SHOOT) == ACTOR_WEAPON_SHOOT) { ok = computer_field_defense(cmd); if (!ok) ok = computer_field_offense(cmd); } else { ok = computer_field_offense(cmd); if (!ok) ok = computer_field_defense(cmd); } computer_field_move(cmd, !ok); } /*--------------------------------------------------------------------------*/ /* computer_field_offense */ /*--------------------------------------------------------------------------*/ int computer_field_offense(COMMAND *cmd) { FIELD_ACTOR *weapon; int dist0, dist1; int dir, dir_last; weapon = field_me->weapon; cmd->f.dir = computer_field_direction(field_he->x, field_he->y); /* dark cloud attacker: closely follow enemy when cloud is active */ if ((weapon->actor->type & ACTOR_MASK) == ACTOR_DARK_CLOUD && weapon->state != 0) { /* ... unless enemy is firing a light cloud */ if ((field_he->weapon->actor->type & ACTOR_MASK) != ACTOR_LIGHT_CLOUD || field_he->weapon->state == 0) { cmd->f.fire = 0; return 1; } } /* otherwise, any attacker: don't try offense until weapon is recharged */ if (!WAS_TIME(weapon, weapon->actor->recharge)) return 0; cmd->f.fire = 1; /* hand/cloud attackers: attack if close enough */ if ((weapon->actor->type & ACTOR_WEAPON_HAND) == ACTOR_WEAPON_HAND || (weapon->actor->type & ACTOR_WEAPON_CLOUD) == ACTOR_WEAPON_CLOUD) { dist0 = abs(field_he->x - field_me->x) * abs(field_he->x - field_me->x) + abs(field_he->y - field_me->y) * abs(field_he->y - field_me->y); /* if ((weapon->actor->type & ACTOR_WEAPON_HAND) == ACTOR_WEAPON_HAND) */ dist1 = CELL_X(1) * CELL_X(1) + CELL_Y(1) * CELL_Y(1); /* else */ /* cloud */ /* dist1 = CELL_X(2) * CELL_X(2) + CELL_Y(2) * CELL_Y(2); */ if (dist0 <= dist1) return 1; } /* shoot attackers: attack if clear line of fire */ if ((weapon->actor->type & ACTOR_WEAPON_SHOOT) == ACTOR_WEAPON_SHOOT) { dir_last = (FIELD_SKILL < FIELD_SKILL_EASY) ? STATE_MOVE_DOWN_RIGHT : STATE_MOVE_RIGHT; for (dir = STATE_MOVE_FIRST; dir <= dir_last; dir++) if (computer_field_collision_course(field_he, field_me, dir)) { cmd->f.dir = dir; return 1; } } cmd->f.dir = 0; cmd->f.fire = 0; return 0; } /*--------------------------------------------------------------------------*/ /* computer_field_defense */ /*--------------------------------------------------------------------------*/ int computer_field_defense(COMMAND *cmd) { FIELD_ACTOR *weapon; FIELD_ACTOR *threat = NULL; int opposite_ok = 0; int state = 0; int dist0, dist1; int i; int x, y; /* avoid missile: if his shoot-type weapon is close enough */ weapon = field_he->weapon; dist0 = abs(weapon->x - field_me->x) * abs(weapon->x - field_me->x) + abs(weapon->y - field_me->y) * abs(weapon->y - field_me->y); dist1 = CELL_X(SHOOT_DISTANCE) * CELL_X(SHOOT_DISTANCE) + CELL_Y(SHOOT_DISTANCE) * CELL_Y(SHOOT_DISTANCE); if ((weapon->actor->type & ACTOR_WEAPON_SHOOT) == ACTOR_WEAPON_SHOOT && weapon->state != 0 && dist0 <= dist1 && computer_field_collision_course(field_me, weapon, weapon->state)) { threat = weapon; state = weapon->state; opposite_ok = 0; } /* avoid actor: if he is close and ready to fire */ /* avoid cloud: if enemy is close and currently firing it */ if (threat == NULL) { dist0 = abs(field_he->x - field_me->x) * abs(field_he->x - field_me->x) + abs(field_he->y - field_me->y) * abs(field_he->y - field_me->y); dist1 = CELL_X(ACTOR_DISTANCE) * CELL_X(ACTOR_DISTANCE) + CELL_Y(ACTOR_DISTANCE) * CELL_Y(ACTOR_DISTANCE); if (dist0 <= dist1 && WAS_TIME(weapon, ACTOR_RECHARGE) && (field_me->weapon->actor->type & ACTOR_WEAPON_SHOOT) == ACTOR_WEAPON_SHOOT) { threat = field_he; opposite_ok = 1; } dist1 = CELL_X(2) * CELL_X(2) + CELL_Y(2) * CELL_Y(2); if (FIELD_SKILL < FIELD_SKILL_EASY && dist0 <= dist1 && WAS_TIME(weapon, ACTOR_RECHARGE) && (field_me->weapon->actor->type & ACTOR_WEAPON_SHOOT) != ACTOR_WEAPON_SHOOT) { threat = field_he; opposite_ok = 1; } } if (threat == NULL) return 0; for (i = 0; i < 1000; i++) { /* retry at most this many times */ cmd->f.dir = STATE_MOVE_UP + (random() % (STATE_MOVE_COUNT - 1)); if (cmd->f.dir == state || (!opposite_ok && cmd->f.dir == state_opposite[state])) continue; x = field_me->x + CELL_X(state_move_x_step[cmd->f.dir]); y = field_me->y + CELL_Y(state_move_y_step[cmd->f.dir]); if (x < CELL_X(1) || x > CELL_X(FIELD_XCELLS - 2) || y < CELL_Y(1) || y > CELL_Y(FIELD_YCELLS - 2)) continue; dist1 = abs(threat->x - x) * abs(threat->x - x) + abs(threat->y - y) * abs(threat->y - y); if (dist1 > dist0) break; } cmd->f.fire = 0; return 1; } /*--------------------------------------------------------------------------*/ /* computer_field_move */ /*--------------------------------------------------------------------------*/ void computer_field_move(COMMAND *cmd, int do_move) { FIELD_ACTOR *weapon; int ok; int x, y; weapon = field_he->weapon; if (do_move) { /* if no move has been made yet */ cmd->f.fire = 0; if (FIELD_SKILL < FIELD_SKILL_MEDIUM) ok = WAS_TIME(weapon, weapon->actor->recharge / 2); else ok = 1; /* always maintain a line of fire */ if (field_frame_time % FIELD_SKILL == 0 && ok) cmd->f.dir = computer_field_direction(field_he->x, field_he->y); else { cmd->f.dir = random() % (STATE_MOVE_COUNT + FIELD_SKILL); if (cmd->f.dir >= STATE_MOVE_COUNT) cmd->f.dir = field_me->last_state; } } else /* if a fire move has been made on easy/medium skill levels */ if (FIELD_SKILL >= FIELD_SKILL_MEDIUM && cmd->f.fire == 1) return; /* avoid running into the path of the enemy's missile */ if ((weapon->actor->type & ACTOR_WEAPON_SHOOT) != ACTOR_WEAPON_SHOOT || weapon->state == 0) return; if (field_frame_time % (random() % (FIELD_SKILL / 10 + 1) + 1) != 0) return; /* if weapon is going to hit us */ x = field_me->x; y = field_me->y; field_me->x += CELL_X(state_move_x_step[cmd->f.dir]); field_me->y += CELL_Y(state_move_y_step[cmd->f.dir]); if (!computer_field_collision_course(field_me, weapon, weapon->state)) { field_me->x = x; field_me->y = y; return; } while (1) { field_me->x = x + CELL_X(state_move_x_step[cmd->f.dir]); field_me->y = y + CELL_Y(state_move_y_step[cmd->f.dir]); if (!computer_field_collision_course(field_me, weapon, weapon->state)) break; cmd->f.dir = STATE_MOVE_UP + (random() % (STATE_MOVE_COUNT - 1)); } cmd->f.fire = 0; field_me->x = x; field_me->y = y; } /*--------------------------------------------------------------------------*/ /* computer_field_collision_course */ /*--------------------------------------------------------------------------*/ int computer_field_collision_course(FIELD_ACTOR *target, FIELD_ACTOR *source, int dir) { int dx, dy; int x, y; char rock; dx = CELL_X(state_move_x_step[dir]); dy = CELL_Y(state_move_y_step[dir]); x = source->x; y = source->y; while (x >= 0 && x < CELL_X(FIELD_XCELLS) && y >= 0 && y < CELL_Y(FIELD_YCELLS)) { if (field_collision(x, y, target->x, target->y)) return 1; rock = field_cells[y / CELL_YSIZE][x / CELL_XSIZE]; if ((rock >> ROCK_IX_SHIFT) != -1 && (rock & ROCK_LUMI_MASK) < ROCK_WALKABLE) break; x += dx; y += dy; } return 0; } /*--------------------------------------------------------------------------*/ /* computer_field_direction */ /*--------------------------------------------------------------------------*/ int computer_field_direction(int x1, int y1) { int dir = 0; int x0, y0; x0 = field_me->x / CELL_XSIZE; y0 = field_me->y / CELL_YSIZE; x1 /= CELL_XSIZE; y1 /= CELL_YSIZE; if (x0 == x1 && y0 == y1) return 0; if (x0 == x1) { if (y0 > y1) { dir = STATE_MOVE_UP; y0 = max(0, y0 - 1); if ((field_cells[y0][x0] >> ROCK_IX_SHIFT) != ROCK_NONE) dir = (random() % 2 == 0) ? STATE_MOVE_UP_LEFT : STATE_MOVE_UP_RIGHT; } else { dir = STATE_MOVE_DOWN; y0 = min(FIELD_YCELLS - 1, y0 + 1); if ((field_cells[y0][x0] >> ROCK_IX_SHIFT) != ROCK_NONE) dir = (random() % 2 == 0) ? STATE_MOVE_DOWN_LEFT : STATE_MOVE_DOWN_RIGHT; } return dir; } if (y0 == y1) { if (x0 > x1) { dir = STATE_MOVE_LEFT; x0 = max(0, x0 - 1); if ((field_cells[y0][x0] >> ROCK_IX_SHIFT) != ROCK_NONE) dir = (random() % 2 == 0) ? STATE_MOVE_UP_LEFT : STATE_MOVE_DOWN_LEFT; } else { dir = STATE_MOVE_RIGHT; x0 = min(FIELD_XCELLS - 1, x0 + 1); if ((field_cells[y0][x0] >> ROCK_IX_SHIFT) != ROCK_NONE) dir = (random() % 2 == 0) ? STATE_MOVE_UP_RIGHT : STATE_MOVE_DOWN_RIGHT; } return dir; } if (x0 > x1) { x0 = max(0, x0 - 1); if (y0 > y1) { dir = STATE_MOVE_UP_LEFT; y0 = max(0, y0 - 1); if ((field_cells[y0][x0] >> ROCK_IX_SHIFT) != ROCK_NONE) dir = (random() % 2 == 0) ? STATE_MOVE_UP : STATE_MOVE_LEFT; } else { dir = STATE_MOVE_DOWN_LEFT; y0 = min(FIELD_YCELLS - 1, y0 + 1); if ((field_cells[y0][x0] >> ROCK_IX_SHIFT) != ROCK_NONE) dir = (random() % 2 == 0) ? STATE_MOVE_DOWN : STATE_MOVE_LEFT; } return dir; } if (x0 < x1) { x0 = min(FIELD_XCELLS - 1, x0 + 1); if (y0 > y1) { dir = STATE_MOVE_UP_RIGHT; y0 = max(0, y0 - 1); if ((field_cells[y0][x0] >> ROCK_IX_SHIFT) != ROCK_NONE) dir = (random() % 2 == 0) ? STATE_MOVE_UP : STATE_MOVE_RIGHT; } else { dir = STATE_MOVE_DOWN_RIGHT; y0 = min(FIELD_YCELLS - 1, y0 + 1); if ((field_cells[y0][x0] >> ROCK_IX_SHIFT) != ROCK_NONE) dir = (random() % 2 == 0) ? STATE_MOVE_DOWN : STATE_MOVE_RIGHT; } return dir; } fprintf(stderr, "computer_field_direction(): i should never get here\n"); return 0; } /*--------------------------------------------------------------------------*/ /* computer_field_stop */ /*--------------------------------------------------------------------------*/ void computer_field_stop(void) { int light, dark; DAMAGE *damage; /* unlike other computer...() functions, this one isn't side-dependant: */ /* whenever it is invoked, field_me is light, and field_he is dark. */ light = field_me->orig_actor->type & ACTOR_MASK; if (light >= ACTOR_AIR_ELEM && light <= ACTOR_WATER_ELEM) light = ACTOR_NUM_LIGHT; /* use last entry in stats array */ else light -= ACTOR_LIGHT_FIRST; /* otherwise get index for entry */ dark = (field_he->orig_actor->type & ACTOR_MASK); if (dark >= ACTOR_AIR_ELEM && dark <= ACTOR_WATER_ELEM) dark = ACTOR_NUM_DARK; /* use last entry in stats array */ else dark -= ACTOR_DARK_FIRST; /* otherwise get index for entry */ damage = &computer_damage[light][dark]; damage->light_sum += field_he->orig_life - field_he->life; damage->dark_sum += field_me->orig_life - field_me->life; damage->light_hits += field_me->num_hits; damage->dark_hits += field_he->num_hits; damage->count++; } /*--------------------------------------------------------------------------*/ /* computer_field_score */ /*--------------------------------------------------------------------------*/ int computer_field_score(ACTOR *attacker, ACTOR *defender, int *attacker_damage, int *defender_damage, int *attacker_hits, int *defender_hits) { int light, dark; DAMAGE *damage; if (attacker->type & ACTOR_LIGHT) { light = attacker->type & ACTOR_MASK; dark = defender->type & ACTOR_MASK; } else { light = defender->type & ACTOR_MASK; dark = attacker->type & ACTOR_MASK; } if (light >= ACTOR_AIR_ELEM && light <= ACTOR_WATER_ELEM) light = ACTOR_NUM_LIGHT; /* use last entry in stats array */ else light -= ACTOR_LIGHT_FIRST; /* otherwise get index for entry */ if (dark >= ACTOR_AIR_ELEM && dark <= ACTOR_WATER_ELEM) dark = ACTOR_NUM_DARK; /* use last entry in stats array */ else dark -= ACTOR_DARK_FIRST; /* otherwise get index for entry */ damage = &computer_damage[light][dark]; if (damage->count == 0) { *attacker_damage = 0; *defender_damage = 0; *attacker_hits = 1; *defender_hits = 1; } else if (attacker->type & ACTOR_LIGHT) { *attacker_damage = damage->light_sum / damage->count; *defender_damage = damage->dark_sum / damage->count; *attacker_hits = max(1, damage->light_hits); *defender_hits = max(1, damage->dark_hits); } else { *attacker_damage = damage->dark_sum / damage->count; *defender_damage = damage->light_sum / damage->count; *attacker_hits = max(1, damage->dark_hits); *defender_hits = max(1, damage->light_hits); } return damage->count; } /*--------------------------------------------------------------------------*/ /* computer_config_read */ /*--------------------------------------------------------------------------*/ void computer_config_read(FILE *fp, COMPUTER_CONFIG *config) { char *path; FILE *fp1; int i, j; DAMAGE *damage; if (fp != NULL) { fscanf(fp, "%32s %d", config->rules, &config->old_board_mode); if (strcmp(config->rules, "easy") == 0) config->data_num = 0; else if (strcmp(config->rules, "medium") == 0) config->data_num = 1; else if (strcmp(config->rules, "hard") == 0) config->data_num = 2; else { fprintf(stderr, "computer_config_read(): unknown rules `%s'\n", config->rules); exit(EXIT_FAILURE); } } if (!computer_damage_ok) { path = malloc(PATH_MAX); sprintf(path, "%s/statistics", DATADIR); fp1 = fopen(path, "r"); if (fp1 == NULL) { fprintf(stderr, "statistics file `%s' not found\n", path); exit(EXIT_FAILURE); } for (i = 0; i < ACTOR_NUM_LIGHT + 1; i++) for (j = 0; j < ACTOR_NUM_DARK + 1; j++) { damage = &computer_damage[i][j]; if (fscanf(fp1, "%d %d %d %d %d", &damage->light_sum, &damage->dark_sum, &damage->light_hits, &damage->dark_hits, &damage->count) == EOF) { fprintf(stderr, "computer_config_read(): not enough statistics\n"); exit(EXIT_FAILURE); } } fclose(fp1); free(path); computer_damage_ok = 1; } } /*--------------------------------------------------------------------------*/ /* computer_config_write */ /*--------------------------------------------------------------------------*/ void computer_config_write(FILE *fp, COMPUTER_CONFIG *config) { fprintf(fp, "%-32s %d\n", (config->rules[0] == 0) ? "easy" : config->rules, config->old_board_mode); #ifdef AUTOPILOT { int i, j; for (i = 0; i < ACTOR_NUM_LIGHT + 1; i++) { for (j = 0; j < ACTOR_NUM_DARK + 1; j++) printf("%5d %5d %5d %5d %5d\n", computer_damage[i][j].light_sum, computer_damage[i][j].dark_sum, computer_damage[i][j].light_hits, computer_damage[i][j].dark_hits, computer_damage[i][j].count); printf("\n"); } } #endif } /****************************************************************************/ /* */ /* GTK+ Stuff */ /* */ /****************************************************************************/ #include #include "gtk-callbacks.h" #include "gtk-interface.h" #include "gtk-support.h" /*--------------------------------------------------------------------------*/ /* variables */ /*--------------------------------------------------------------------------*/ static COMPUTER_CONFIG *orig_config, work_config; static GtkWidget *window; /*--------------------------------------------------------------------------*/ /* computer_config_edit */ /*--------------------------------------------------------------------------*/ void computer_config_edit(COMPUTER_CONFIG *_config) { GtkWidget *widget; orig_config = _config; memcpy(&work_config, orig_config, sizeof(COMPUTER_CONFIG)); window = create_computer_window(); widget = lookup_widget(window, work_config.rules); if (widget != NULL) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE); if (orig_config->old_board_mode) { widget = lookup_widget(window, "old_board_mode"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE); } gtk_widget_show(window); } /*--------------------------------------------------------------------------*/ /* computer_skill_toggled */ /*--------------------------------------------------------------------------*/ void computer_skill_toggled(GtkToggleButton *button, gpointer data) { strcpy(work_config.rules, data); } /*--------------------------------------------------------------------------*/ /* computer_ok_clicked */ /*--------------------------------------------------------------------------*/ void computer_ok_clicked(GtkButton *button, gpointer data) { GtkWidget *widget; memcpy(orig_config, &work_config, sizeof(COMPUTER_CONFIG)); widget = lookup_widget(window, "old_board_mode"); orig_config->old_board_mode = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); gtk_widget_destroy(window); } /*--------------------------------------------------------------------------*/ /* computer_cancel_clicked */ /*--------------------------------------------------------------------------*/ void computer_cancel_clicked(GtkObject *object, gpointer data) { gtk_widget_destroy(window); }