/* awesomely awesome AI by Mason */ include "lib_fsm.bmd" include "stdlib.bmd" /*************************************** * utility functions ***************************************/ moveDirection array [4] directions; bool is_precomputed; point curr_position; point last_position; point STAGE_CENTER; point STAGE_DIM; point ORIGIN; point START_POS; TileType array [] array [] curr_map; void init(point p) { directions[0] = moveDirection.North; directions[1] = moveDirection.East; directions[2] = moveDirection.South; directions[3] = moveDirection.West; ORIGIN.x = 0; ORIGIN.y = 0; START_POS.x = p.x; START_POS.y = p.y; last_position = ORIGIN; curr_position = ORIGIN; // see precomp state is_precomputed = false; } // get the direction that comes from turning the given direction by a certain // amount. moveDirection turn_by(moveDirection dir, int q) { for (int i = 0; i < 4; i += 1;) { if (directions[i] = dir) { return directions[(i+q)%4]; } } return moveDirection.Void; } /** * add a point to a direction to get a new point */ point add_direction(point p, moveDirection dir) { point ret; ret.x = p.x; ret.y = p.y; if (dir = moveDirection.North) { if (p.y != 0) { ret.y -= 1; } } else if (dir = moveDirection.South) { if (p.y != (STAGE_DIM.y - 1)) { ret.y += 1; } } else if (dir = moveDirection.East) { if (p.x != (STAGE_DIM.x - 1)) { ret.x += 1; } } else if (dir = moveDirection.West) { if (p.x != 0) { ret.x -= 1; } } return ret; } void no_op_void() { } void no_op_point(point p) { } bool always() { return true; } int absi(int x) { if (x > 0) { return x; } else { return -x; } } float absf(float x) { if (x > 0.0) { return x; } else { return -x; } } move default_move() { move def; def.placeBomb = false; def.detonate = false; def.direction = moveDirection.Void; return def; } string string_of_direction(moveDirection d) { if (d = moveDirection.East) { return "East"; } else if (d = moveDirection.West) { return "West"; } else if (d = moveDirection.North) { return "North"; } else if (d = moveDirection.South) { return "South"; } else { return "void"; } } // returns true if a tile is clear bool is_clear(point p) { if (curr_map[p.x][p.y] = TileType.Empty) { return true; } return false; } //////////////////////////////////////////////////////////// //// precomp state //// precomputes values used by other states that rely on the environment //////////////////////////////////////////////////////////// /** computes the stage dimensions and center **/ move precomp_move(environment env) { int x_len; !(array_length(env.map), x_len); STAGE_DIM.x = x_len; int y_len; !(array_length(env.map), y_len); STAGE_DIM.y = y_len; STAGE_CENTER.x = trunc (STAGE_DIM.x / 2); STAGE_CENTER.y = trunc (STAGE_DIM.y / 2); !(print("stage center: ")); !(println(STAGE_CENTER)); is_precomputed = true; return default_move(); } bool precomp_done(fsmStateInformation f) { return is_precomputed; } /*************************************** * direction choosing **************************************/ moveDirection move_toward(point start, point dest) { point diff; diff.x = dest.x - start.x; diff.y = dest.y - start.y; !(print("diff: ")); !(println(diff)); moveDirection first_dir; moveDirection second_dir; // make sure we're not at a destination if ((diff.x = 0) and (diff.y = 0)) { return moveDirection.Void; } // weight based on x direction if ((absi(diff.x) > absi(diff.y)) or (absi(diff.x) = absi(diff.y))) { if (diff.x > 0) { first_dir = moveDirection.East; } else { first_dir = moveDirection.West; } if (diff.y > 0) { second_dir = moveDirection.South; } else {second_dir = moveDirection.North; } } // weight based on y-direction else { if (diff.y > 0) { first_dir = moveDirection.South; } else { first_dir = moveDirection.North; } if (diff.x > 0) { second_dir = moveDirection.East; } else {second_dir = moveDirection.West; } } if (is_clear(add_direction(start, first_dir))) { return first_dir; } if (is_clear(add_direction(start, second_dir))) { return second_dir; } return turn_by(first_dir, 1); } /** * if an object isn't in the way, return the original direction, otherwise * return a dodging direction */ /*************************************** * universal fields **************************************/ moveDirection headed; int untilSwitch; point destination; int since_moved; void update_fields(environment e) { last_position = curr_position; curr_position = e.playerStatus.position; if ((last_position.x = curr_position.x) and (last_position.y = curr_position.y)) { since_moved += 1; } else { since_moved = 0; } curr_map = e.map; } void chasing_init() { last_position = ORIGIN; destination.x = STAGE_CENTER.x; destination.y = STAGE_CENTER.y; untilSwitch = 0; since_moved = 0; } /*************************************** * chasing **************************************/ move chasing_move_request(environment e) { update_fields(e); move returnMove = default_move(); if ((untilSwitch < 0) or (untilSwitch = 0)) { headed = move_toward(e.playerStatus.position, STAGE_CENTER); untilSwitch = 1; } // if we're running into something, turn and try again point new_pos = add_direction(curr_position, headed); if ((not is_clear(new_pos))) { /*if (curr_map[new_pos.x][new_pos.y] = TileType.Brick) { returnMove.direction = moveDirection.Void; return returnMove; }*/ headed = turn_by(headed, 1); returnMove.direction = headed; untilSwitch = 1; return returnMove; } returnMove.direction = headed; untilSwitch -= 1; return returnMove; } bool blocked_by_brick(fsmStateInformation f) { point p = add_direction(f.latestPosition, headed); if (f.latestEnvironment.map[p.x][p.y] = TileType.Brick) { return true; } return false; } bool blocked_by_block(fsmStateInformation f) { point p = add_direction(f.latestPosition, headed); if (f.latestEnvironment.map[p.x][p.y] = TileType.Block) { return true; } return false; } bool stuck(fsmStateInformation f) { return (since_moved > 5); } /*************************************** * brick clearing **************************************/ point safe_position; point last_safe_position; bool laid_bomb; bool bomb_cleared; point bomb_position; void clearing_init() { !(println("clearing")); laid_bomb = false; bomb_cleared = false; // find a safe position safe_position = START_POS; } bool safe_from_bomb() { int xd = absi(bomb_position.x - curr_position.x); int yd = absi(bomb_position.y - curr_position.y); if ((xd != 0) and (yd != 0)) { return true; } if (xd > 3) {return true;} if (yd > 3) { return true; } return false; } move clearing_move_request(environment env) { update_fields(env); move return_move = default_move(); // if we haven't laid a bomb lay it if (not laid_bomb) { !(println("laying bomb...")); laid_bomb = true; bomb_position.x = curr_position.x; bomb_position.y = curr_position.y; return_move.placeBomb = true; return return_move; } if (safe_from_bomb()) { return return_move; } return_move.direction = move_toward(curr_position, safe_position); // check that we're safe if ((return_move.direction != moveDirection.Void) and (is_clear(add_direction(curr_position, return_move.direction)))) { return return_move; } else { // change directions moveDirection new_dir = move_toward(curr_position, bomb_position); // check that it's clear for (int i = 0; i < 3; i += 1;) { new_dir = turn_by(new_dir, 1); if (is_clear(add_direction(curr_position, new_dir))) { safe_position = add_direction(safe_position, new_dir); return_move.direction = new_dir; return return_move; } } // otherwise just go back and try to find a better position new_dir = turn_by(new_dir, 1); safe_position = add_direction(safe_position, new_dir); return_move.direction = new_dir; return return_move; } } bool clearing_bomb_detonated(fsmStateInformation f) { return bomb_cleared; } void clearing_signal_detonation(point p) { bomb_cleared = true; } /*************************************** * State Machine **************************************/ stateMachine gungho { stateMachineInit: init; state precomp { onInit: no_op_void; onMoveRequest: precomp_move; onTeammateDeath: no_op_void; onDeath: no_op_void; onBombDetonate: no_op_point; stateTransition { transitionPredicates: precomp_done; transitionTargets: chasing 1.0; } } state chasing { onInit: chasing_init; onMoveRequest: chasing_move_request; onTeammateDeath: no_op_void; onDeath: no_op_void; onBombDetonate: no_op_point; stateTransition { transitionPredicates: blocked_by_brick; transitionTargets: clearing 1.0; } stateTransition { transitionPredicates: stuck; transitionTargets: clearing 1.0; } } state clearing { onInit: clearing_init; onMoveRequest: clearing_move_request; onTeammateDeath: no_op_void; onDeath: no_op_void; onBombDetonate: clearing_signal_detonation; stateTransition { transitionPredicates: clearing_bomb_detonated; transitionTargets: chasing 1.0; } } } void main() { finiteStateMachine fsm = gungho(); registerFSM(fsm); }