forked from aichallenge/aichallenge
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Completed a functioning ant wars simulation.
- Loading branch information
Showing
10 changed files
with
269 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
*.pyc | ||
*~ | ||
*.gif | ||
*.png |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
|
||
|
||
class Ant: | ||
def __init__(self, x, y, owner): | ||
self.x = x | ||
self.y = y | ||
self.owner = owner |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
|
||
|
||
class AntList: | ||
def __init__(self): | ||
self.ants = [] | ||
|
||
def add(self, ant): | ||
self.ants.append(ant) | ||
|
||
def nearby_ants(self, x, y, r): | ||
nearby = [] | ||
for ant in self.ants: | ||
if abs(ant.x - x) + abs(ant.y - y) <= r: | ||
nearby.append(ant) | ||
return nearby | ||
|
||
def __iter__(self): | ||
self.current_ant = 0 | ||
return self | ||
|
||
def __len__(self): | ||
return len(self.ants) | ||
|
||
def __getitem__(self, key): | ||
return self.ants[key] | ||
|
||
def next(self): | ||
if self.current_ant >= len(self.ants): | ||
raise StopIteration | ||
else: | ||
self.current_ant += 1 | ||
return self.ants[self.current_ant - 1] | ||
|
||
def get_by_location(self, x, y): | ||
for ant in self.ants: | ||
if ant.x == x and ant.y == y: | ||
return ant | ||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import copy | ||
|
||
from map import * | ||
from random_bot import * | ||
from hunter_bot import * | ||
|
||
def switch_players(m, a, b): | ||
m = copy.deepcopy(m) | ||
for ant in m.ants: | ||
if ant.owner == a: | ||
ant.owner = b | ||
elif ant.owner == b: | ||
ant.owner = a | ||
return m | ||
|
||
def play_game(map_filename, players): | ||
m = Map(map_filename) | ||
initial_food_density = 0.01 | ||
food_amount = int(initial_food_density * m.land_area) | ||
m.randomly_place_food(food_amount) | ||
turn_count = 1 | ||
while not m.game_over(): | ||
print "turn:", turn_count | ||
img = m.render() | ||
scale = 4 | ||
new_size = (img.size[0] * scale, img.size[1] * scale) | ||
img = img.resize(new_size) | ||
img.save("playback/frame" + str(turn_count).zfill(5) + ".png") | ||
for i, player in enumerate(players): | ||
player_number = i + 1 | ||
reflected_map = switch_players(m, player_number, 1) | ||
orders = player.do_turn(reflected_map) | ||
for order in orders: | ||
m.do_order(order) | ||
m.do_turn() | ||
turn_count += 1 | ||
|
||
players = [RandomBot(), RandomBot(), RandomBot(), HunterBot()] | ||
play_game("four_map.png", players) |
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import math | ||
import random | ||
|
||
from player import Player | ||
|
||
class HunterBot(Player): | ||
def do_turn(self, m): | ||
orders = [] | ||
my_ants = [a for a in m.ants if a.owner == 1] | ||
for ant in my_ants: | ||
if ant.owner != 1: | ||
continue | ||
x, y = ant.x, ant.y | ||
candidates = [ | ||
(x, y, x + 1, y), | ||
(x, y, x - 1, y), | ||
(x, y, x, y + 1), | ||
(x, y, x, y - 1) | ||
] | ||
closest_enemy = None | ||
closest_distance = 999999.0 | ||
for other_ant in m.ants: | ||
if other_ant.owner == 1: | ||
continue | ||
dx = ant.x - other_ant.x | ||
dy = ant.y - other_ant.y | ||
dist = math.sqrt(dx ** 2 + dy ** 2) | ||
if dist < closest_distance: | ||
closest_distance = dist | ||
closest_enemy = other_ant | ||
best_move = None | ||
closest_distance = 999999.0 | ||
for c in candidates: | ||
if c[2] < 0 or c[3] < 0: | ||
continue | ||
if c[2] >= m.width or c[3] >= m.height: | ||
continue | ||
if not m.passable[c[2]][c[3]]: | ||
continue | ||
dx = c[2] - closest_enemy.x | ||
dy = c[3] - closest_enemy.y | ||
dist = math.sqrt(dx ** 2 + dy ** 2) | ||
if dist < closest_distance: | ||
closest_distance = dist | ||
best_move = c | ||
if best_move is not None: | ||
orders.append(best_move) | ||
return orders |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import math | ||
|
||
from ant import * | ||
from ant_list import * | ||
import Image | ||
import random | ||
|
||
class Map: | ||
def __init__(self, filename): | ||
self.passable = [] | ||
self.ants = AntList() | ||
image = Image.open(filename) | ||
(self.width, self.height) = image.size | ||
self.num_players = 0 | ||
self.land_area = 0 | ||
self.water_area = 0 | ||
for x in range(self.width): | ||
self.passable.append([]) | ||
for y in range(self.height): | ||
pixel = image.getpixel((x, y)) | ||
if pixel[0] > 0: | ||
self.passable[x].append(True) | ||
self.num_players += 1 | ||
self.ants.add(Ant(x, y, self.num_players)) | ||
self.land_area += 1 | ||
elif pixel[1] > 0: | ||
self.passable[x].append(True) | ||
self.land_area += 1 | ||
elif pixel[2] > 0: | ||
self.passable[x].append(False) | ||
self.water_area += 1 | ||
|
||
def randomly_place_food(self, amount=1): | ||
for i in range(amount): | ||
for j in range(10): | ||
x = random.randrange(self.width) | ||
y = random.randrange(self.height) | ||
if self.passable[x][y] and self.ants.get_by_location(x, y) is None: | ||
self.ants.add(Ant(x, y, 0)) | ||
break | ||
|
||
def render(self): | ||
water_color = (0, 0, 255) | ||
land_color = (0, 255, 0) | ||
player_colors = [ | ||
(139, 69, 19), | ||
(255, 0, 0), | ||
(255, 255, 255), | ||
(0, 0, 0), | ||
(255, 255, 0), | ||
(255, 0, 255), | ||
(255, 127, 0) | ||
] | ||
image = Image.new("RGB", ((self.width, self.height))) | ||
for x in range(self.width): | ||
for y in range(self.height): | ||
ant = self.ants.get_by_location(x, y) | ||
if ant is not None: | ||
image.putpixel((x, y), player_colors[ant.owner]) | ||
elif self.passable[x][y]: | ||
image.putpixel((x, y), land_color) | ||
else: | ||
image.putpixel((x, y), water_color) | ||
return image | ||
|
||
def remaining_players(self): | ||
return set([a.owner for a in self.ants]) | ||
|
||
def game_over(self): | ||
return len(self.remaining_players()) <= 1 | ||
|
||
def do_order(self, order): | ||
x1, y1, x2, y2 = order | ||
ant = self.ants.get_by_location(x1, y1) | ||
if ant is None: | ||
return | ||
defender = self.ants.get_by_location(x2, y2) | ||
if defender is not None: | ||
return | ||
ant.x, ant.y = x2, y2 | ||
|
||
def do_turn(self): | ||
for i in range(len(self.ants)): | ||
for j in range(i + 1, len(self.ants)): | ||
if i >= len(self.ants) or j >= len(self.ants): | ||
continue | ||
a = self.ants[i] | ||
b = self.ants[j] | ||
dist = math.sqrt((a.x - b.x) ** 2 + (a.y - b.y) ** 2) | ||
if dist <= 3.000001: | ||
if a.owner == 0 and b.owner == 0: | ||
pass | ||
elif a.owner == b.owner: | ||
pass | ||
elif a.owner == 0: | ||
a.owner = b.owner | ||
elif b.owner == 0: | ||
b.owner = a.owner | ||
else: | ||
self.ants.ants.pop(j) | ||
self.ants.ants.pop(i) |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
class Player: | ||
def do_turn(self, map): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import random | ||
|
||
from player import Player | ||
|
||
class RandomBot(Player): | ||
def do_turn(self, m): | ||
orders = [] | ||
for ant in m.ants: | ||
if ant.owner != 1: | ||
continue | ||
x, y = ant.x, ant.y | ||
candidates = [ | ||
(x, y, x + 1, y), | ||
(x, y, x - 1, y), | ||
(x, y, x, y + 1), | ||
(x, y, x, y - 1) | ||
] | ||
possible = [] | ||
for c in candidates: | ||
if c[2] < 0 or c[3] < 0: | ||
continue | ||
if c[2] >= m.width or c[3] >= m.height: | ||
continue | ||
if not m.passable[c[2]][c[3]]: | ||
continue | ||
possible.append(c) | ||
if len(possible) > 0: | ||
orders.append(random.choice(possible)) | ||
return orders |