# Phil Fritzsche
# Gary Parker
# Connecticut College Computer Science
import math
import sys
import thread
import threading
import time
import xpai
sys.setcheckinterval(0)
MAX_TURN = 20
#chromosome = '100100111100011000101100000011111101101001010100000000100100000110011110'
chromosome = '000000000000000000000000000000000000000000000000000000000000000000000000'
class Struct(dict):
"""A dictionary whose values can also be accessed as attributes."""
def __init__(self, **kwargs):
self.update(kwargs)
def __getattr__(self, name):
"""Retrieves the value for the given name, if it exists. Returns
None if it does not."""
if name in self:
return self[name]
def __setattr__(self, name, value):
"""Sets the value for the given name to the given value."""
self[name] = value
def gene_to_num(gene):
dec_num = 0
for i in gene:
dec_num = (dec_num << 1) | int(i)
return dec_num
gene = Struct()
def load_gene(chromosome):
xpai.talk(str(chromosome))
global gene
gene = Struct(
span=gene_to_num(chromosome[0:4]),
offset_inc=gene_to_num(chromosome[4:8]) + 1,
samespread=gene_to_num(chromosome[8:12]),
wall_span1=gene_to_num(chromosome[12:16]) * 4,
wall_span2=gene_to_num(chromosome[16:20]) * 4,
vd_bullet_dist=gene_to_num(chromosome[20:24]) * 4,
close_wall_speed=gene_to_num(chromosome[24:28]) * 2,
c_angle_before_thrust=gene_to_num(chromosome[28:32]) * 10,
d_bullet_dist=(gene_to_num(chromosome[32:36]) * 4) + \
(gene_to_num(chromosome[20:24]) * 4),
medium_wall_speed=gene_to_num(chromosome[36:40]) * 4,
m_angle_before_thrust=gene_to_num(chromosome[40:44]) * 10,
wall_avoid_angle=gene_to_num(chromosome[44:48]),
screen_thrust_speed=gene_to_num(chromosome[48:52]),
radar_no_thrust_speed=gene_to_num(chromosome[52:56]),
ship_error_to_shoot=gene_to_num(chromosome[56:60]) * 2,
radar_error_to_shoot=gene_to_num(chromosome[60:64]) * 2,
vd_dodge_bullet_angle=gene_to_num(chromosome[64:68]) * 10,
d_dodge_bullet_angle=gene_to_num(chromosome[68:72]) * 10
)
def wall_feeler(range, degree):
"""Checks for walls within the given range at the given degree from the
ship's current position. Uses absolute scale for degrees."""
delta_x = xpai.self_x() + range * math.cos(math.radians(degree))
delta_y = xpai.self_y() + range * math.sin(math.radians(degree))
res = xpai.wallbetween(xpai.self_x(), xpai.self_y(), delta_x, delta_y)
return res == -1 and range or res
def is_shot_behind_wall(n, span):
c1 = bool(wall_feeler(
xpai.shot_dist(n), xpai.shot_xdir(n)) < xpai.shot_dist(n))
c2 = bool(wall_feeler(
xpai.shot_dist(n), xpai.angleadd(
xpai.shot_xdir(n), span)) < xpai.shot_dist(n))
c3 = bool(wall_feeler(
xpai.shot_dist(n), xpai.angleadd(
xpai.shot_xdir(n), -span)) < xpai.shot_dist(n))
return c1 and c2 and c3
def is_ship_behind_wall(n):
return bool(wall_feeler(
xpai.ship_dist(n), xpai.ship_xdir(n)) < xpai.ship_dist(n))
def is_radar_ship_behind_wall(n):
return bool(wall_feeler(
xpai.radar_dist(n), xpai.radar_xdir(n)) < xpai.radar_dist(n))
def wall_avoid_turn_dir_ship(n, offset_inc):
range = xpai.ship_dist(n)
degree = xpai.ship_xdir(n)
return wall_avoid_turn_dir_helper(range, degree, 0, offset_inc)
def wall_avoid_turn_dir_radar(n, offset_inc):
range = xpai.radar_dist(n)
degree = xpai.radar_xdir(n)
return wall_avoid_turn_dir_helper(range, degree, 0, offset_inc)
def wall_avoid_turn_dir_helper(rng, deg, os, os_inc):
if os > 180:
return -1000
elif wall_feeler(rng, deg + os) == rng:
return deg + os
elif wall_feeler(rng, deg - os) == rng:
return deg - os
return wall_avoid_turn_dir_helper(rng, deg, os + os_inc, os_inc)
def screen_enemy_num(n):
"""Returns the number of the nearest enemy on screen, or -1 if no
enemy is currently on screen."""
if xpai.ship_x(n) == -1:
return -1
elif xpai.teamplay() == 1 and xpai.self_team() != xpai.ship_team(n):
return n
return screen_enemy_num(n + 1)
def screen_enemy_num2(n, first):
if first < 0:
return -1
elif xpai.ship_x(n) == -1:
return -1
elif (n != first and xpai.teamplay == 1 and
xpai.self_team() != xpai.ship_team(n)):
return n
return screen_enemy_num(n + 1)
def radar_enemy_num2(n, first):
if first < 0:
return -1
elif xpai.radar_x(n) == -1:
return -1
elif n != first and xpai.radar_xdir(n) != -1:
return n
return radar_enemy_num(n + 1)
def is_same(x, y, spread):
return abs(x - y) <= spread
def radar_enemy_num(n):
"""Returns the number of the nearest enemy on radar, or -1 if no
enemy currently exists on radar."""
if xpai.radar_x(n) == -1:
return -1
elif xpai.radar_xdir(n) != -1:
return n
return radar_enemy_num(n + 1)
def change_heading(dir):
"""Turns the ship relative to its heading."""
xpai.self_turn(xpai.anglediff(xpai.self_heading(), dir))
current_score = xpai.self_score()
reset_flag = False
frames = 0
kills = 0
final_fitness = 0
start_time = time.time()
pre_life = True
is_done = threading.Event()
def reset_all():
global current_score
global frames
global kills
global final_fitness
global is_done
global reset_flag
global start_time
current_score = xpai.self_score()
frames = 0
kills = 0
final_fitness = 0
start_time = time.time()
is_done.clear()
reset_flag = False
def ai_main():
"""Main function for the script; called once every frame of the game."""
global is_done
global pre_life
if pre_life and not xpai.self_alive():
return
pre_life = False
global current_score
if xpai.self_score() > current_score and not is_done.isSet():
global kills
kills += 1
elif xpai.self_score() < current_score and not is_done.isSet():
global start_time
start_time -= 20
current_score = xpai.self_score()
#print 'is_done: %r' % is_done
#print 'final_fitness: %r' % final_fitness
#print 'kills: %r' % kills
#print 'frames: %r' % frames
if xpai.self_alive():
global frames
frames += 1
ship_num = screen_enemy_num(0)
ship_num2 = screen_enemy_num2(0, ship_num)
radar_ship_num = radar_enemy_num(0)
radar_ship_num2 = radar_enemy_num2(0, radar_ship_num)
wf_r1 = wall_feeler(600, xpai.angleadd(
xpai.self_track(), -gene.wall_span1))
wf_l1 = wall_feeler(600, xpai.angleadd(
xpai.self_track(), gene.wall_span1))
wf_r2 = wall_feeler(600, xpai.angleadd(
xpai.self_track(), -gene.wall_span2))
wf_l2 = wall_feeler(600, xpai.angleadd(
xpai.self_track(), gene.wall_span2))
# Dodge bullet if very close
#print 'alive'
if (xpai.shot_alert(0) > -1 and
xpai.shot_alert(0) < gene.vd_bullet_dist and
not is_shot_behind_wall(0, gene.span)):
#print 'bullet vd'
added_ang = xpai.angleadd(xpai.shot_idir(0),
gene.vd_dodge_bullet_angle)
change_heading(added_ang)
xpai.self_thrust(1)
elif (xpai.shot_alert(1) > -1 and
xpai.shot_alert(1) < gene.vd_bullet_dist and
not is_shot_behind_wall(1, gene.span)):
#print 'bullet2 vd'
added_ang = xpai.angleadd(xpai.shot_idir(1),
gene.vd_dodge_bullet_angle)
change_heading(added_ang)
xpai.self_thrust(1)
# Two wall feelers are close
elif (is_same(wf_r1, wf_l1, gene.samespread) and
wf_r1 < gene.close_wall_speed * xpai.self_vel() and
xpai.self_vel() > 1):
#print 'both wall feelers close'
turn_amt = xpai.angleadd(180, xpai.self_track())
change_heading(turn_amt)
if (abs(xpai.anglediff(xpai.self_heading(), turn_amt)) <
gene.c_angle_before_thrust):
xpai.self_thrust(1)
# Right wall feeler is closer than the left
elif (wf_r1 < wf_l1 and
wf_r1 < gene.close_wall_speed * xpai.self_vel() and
xpai.self_vel() > 1):
#print 'right wall feeler close'
turn_amt = xpai.angleadd(180, xpai.angleadd(
-gene.wall_span1, xpai.self_track()))
change_heading(turn_amt)
if (abs(xpai.anglediff(xpai.self_heading(), turn_amt)) <
gene.c_angle_before_thrust):
xpai.self_thrust(1)
# Left wall feeler is closer than the right
elif (wf_r1 > wf_l1 and
wf_l1 < gene.close_wall_speed * xpai.self_vel() and
xpai.self_vel() > 1):
#print 'left wall feeler close'
turn_amt = xpai.angleadd(180, xpai.angleadd(
gene.wall_span1, xpai.self_track()))
change_heading(turn_amt)
if (abs(xpai.anglediff(xpai.self_heading(), turn_amt)) <
gene.c_angle_before_thrust):
xpai.self_thrust(1)
# Dodge bullet if close [but not very]
elif (xpai.shot_alert(0) > -1 and
xpai.shot_alert(0) < gene.d_bullet_dist and
not is_shot_behind_wall(0, gene.span)):
#print 'bullet d'
added_ang = xpai.angleadd(xpai.shot_idir(0),
gene.d_dodge_bullet_angle)
change_heading(added_ang)
xpai.self_thrust(1)
elif (xpai.shot_alert(1) > -1 and
xpai.shot_alert(1) < gene.d_bullet_dist and
not is_shot_behind_wall(1, gene.span)):
#print 'bullet2 d'
added_ang = xpai.angleadd(xpai.shot_idir(1),
gene.d_dodge_bullet_angle)
change_heading(added_ang)
xpai.self_thrust(1)
# Two wall feelers are at medium distance
elif (is_same(wf_r2, wf_l2, gene.samespread) and
wf_r2 < gene.medium_wall_speed * xpai.self_vel() and
xpai.self_vel() > 1):
#print 'both wall feelers medium'
turn_amt = xpai.angleadd(180, xpai.self_track())
change_heading(turn_amt)
if (abs(xpai.anglediff(xpai.self_heading(), turn_amt)) <
gene.m_angle_before_thrust):
xpai.self_thrust(1)
# Right wall feeler is closer than the left
elif (wf_r2 < wf_l2 and
wf_r2 < gene.medium_wall_speed * xpai.self_vel() and
xpai.self_vel() > 1):
#print 'right wall feeler medium'
turn_amt = xpai.angleadd(180, xpai.angleadd(
-gene.wall_span2, xpai.self_track()))
change_heading(turn_amt)
if (abs(xpai.anglediff(xpai.self_heading(), turn_amt)) <
gene.m_angle_before_thrust):
xpai.self_thrust(1)
# Left wall feeler is closer than the right
elif (wf_l2 < wf_r2 and
wf_l2 < gene.medium_wall_speed * xpai.self_vel() and
xpai.self_vel() > 1):
#print 'left wall feeler medium'
turn_amt = xpai.angleadd(180, xpai.angleadd(
gene.wall_span2, xpai.self_track()))
change_heading(turn_amt)
if (abs(xpai.anglediff(xpai.self_heading(), turn_amt)) <
gene.m_angle_before_thrust):
xpai.self_thrust(1)
# Turn towards nearest enemy ship on screen
elif ship_num > -1 and not is_ship_behind_wall(ship_num):
#print 'turn towards nearest enemy ship on screen'
change_heading(xpai.ship_aimdir(ship_num))
# Turn towards second nearest enemy ship on screen
elif ship_num2 > -1 and not is_ship_behind_wall(ship_num2):
#print 'turn towards nearest enemy ship2 on screen'
change_heading(xpai.ship_aimdir(ship_num2))
# Turn, thrust towards nearest enemy
elif (ship_num > -1 and
not wall_avoid_turn_dir_ship(ship_num, gene.offset_inc) == -1000):
#print 'turn and thrust towards nearest enemy'
turn_dir = wall_avoid_turn_dir_ship(ship_num, gene.offset_inc)
turn_amt = xpai.anglediff(xpai.self_heading(), turn_dir)
xpai.self_turn(turn_amt)
if (abs(turn_amt) < gene.wall_avoid_angle and
xpai.self_vel() < gene.screen_thrust_speed):
xpai.self_thrust(1)
# Turn, thrust towards nearest enemy
elif (ship_num2 > -1 and
not wall_avoid_turn_dir_ship(ship_num2, gene.offset_inc) == -1000):
#print 'turn and thrust towards nearest enemy2'
turn_dir = wall_avoid_turn_dir_ship(ship_num2, gene.offset_inc)
turn_amt = xpai.anglediff(xpai.self_heading(), turn_dir)
xpai.self_turn(turn_amt)
if (abs(turn_amt) < gene.wall_avoid_angle and
xpai.self_vel() < gene.screen_thrust_speed):
xpai.self_thrust(1)
# Turn to radar ship, thrust if not going too fast. Otherwise, shoot.
elif (radar_ship_num > -1 and
not is_radar_ship_behind_wall(radar_ship_num) and
xpai.self_vel() > gene.radar_no_thrust_speed):
#print 'turn to radar ship and thrust if not going too fast 1'
# enemy 0 on radar, no wall and currently moving fast
change_heading(xpai.radar_xdir(radar_ship_num))
elif (radar_ship_num > -1 and
not is_radar_ship_behind_wall(radar_ship_num)):
#print 'turn to radar ship and thrust if not going too fast 2'
# enemy 0 on radar, no wall, and not currently moving fast
change_heading(xpai.radar_xdir(radar_ship_num))
xpai.self_thrust(1)
elif (radar_ship_num2 > -1 and
not is_radar_ship_behind_wall(radar_ship_num2) and
xpai.self_vel() > gene.radar_no_thrust_speed):
#print 'turn to radar ship and thrust if not going too fast 3'
# enemy 1 is on radar, no wall, and currently moving fast
change_heading(radar_ship_num2)
elif (radar_ship_num2 > -1 and
not is_radar_ship_behind_wall(radar_ship_num2)):
#print 'turn to radar ship and thrust if not going too fast 4'
# enemy 1 on radar, no wall, and not currently moving fast
change_heading(xpai.radar_xdir(radar_ship_num2))
xpai.self_thrust(1)
elif (radar_ship_num > -1 and
wall_avoid_turn_dir_radar(radar_ship_num, gene.offset_inc) != -1000):
#print 'turn to radar ship and thrust if not going too fast 5'
# enemy 0 on radar and behind wall
turn_dir = wall_avoid_turn_dir_radar(radar_ship_num, gene.offset_inc)
turn_amt = xpai.anglediff(xpai.self_heading(), turn_dir)
xpai.self_turn(turn_amt)
if abs(turn_amt) < 5 and xpai.self_vel() < 20:
xpai.self_thrust(1)
angle_to_ship_diff = xpai.anglediff(
xpai.self_heading(), xpai.ship_aimdir(ship_num))
shoot_cond1 = bool(
ship_num > -1 and
abs(angle_to_ship_diff) < gene.ship_error_to_shoot and
not is_ship_behind_wall(ship_num))
#print 'shoot cond1'
angle_to_ship2_diff = xpai.anglediff(
xpai.self_heading(), xpai.ship_aimdir(ship_num2))
shoot_cond2 = bool(
ship_num2 > -1 and
abs(angle_to_ship2_diff) < gene.ship_error_to_shoot and
not is_ship_behind_wall(ship_num2))
#print 'shoot cond2'
angle_to_rship_diff = xpai.anglediff(
xpai.self_heading(), xpai.radar_xdir(radar_ship_num))
shoot_cond3 = bool(
radar_ship_num > -1 and
abs(angle_to_rship_diff) < gene.radar_error_to_shoot and
not is_radar_ship_behind_wall(radar_ship_num))
#print 'shoot cond3'
angle_to_rship2_diff = xpai.anglediff(
xpai.self_heading(), xpai.radar_xdir(radar_ship_num2))
shoot_cond4 = bool(
radar_ship_num2 > -1 and
abs(angle_to_rship2_diff) < gene.radar_error_to_shoot and
not is_radar_ship_behind_wall(radar_ship_num2))
#print 'shoot cond4'
if shoot_cond1 or shoot_cond2 or shoot_cond3 or shoot_cond4:
#print 'shooting'
xpai.self_shoot(1)
#print 'after shooting'
elif reset_flag:
reset_all()
pre_life = True
elif not is_done.isSet() and abs(time.time() - start_time) > 120:
global final_fitness
final_fitness = frames + (1000 * kills)
pre_life = True
is_done.set()
else:
xpai.talk(str(frames))
xpai.talk(str(abs(time.time() - start_time)))
pre_life = True
def set_reset(chrom):
global reset_flag
global is_done
load_gene(chrom)
reset_flag = True
is_done.clear()
def get_fitness():
xpai.talk(str(final_fitness))
return final_fitness
def launch(fps):
# Initialize XPilot
xpai.set_AImain(ai_main)
xpai.setmaxturn(MAX_TURN)
xpai.setargs('-join localhost -port 45%s -name Expert' % fps)
load_gene(chromosome)
thread.start_new_thread(xpai.launch, ())