Skip to content

Commit

Permalink
Completed a functioning ant wars simulation.
Browse files Browse the repository at this point in the history
  • Loading branch information
j3camero committed Jan 12, 2011
1 parent d3fe908 commit 7f614da
Show file tree
Hide file tree
Showing 10 changed files with 269 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*.pyc
*~
*.gif
*.png
7 changes: 7 additions & 0 deletions ant.py
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
38 changes: 38 additions & 0 deletions ant_list.py
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
39 changes: 39 additions & 0 deletions engine.py
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)
Binary file added four_map.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 48 additions & 0 deletions hunter_bot.py
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
101 changes: 101 additions & 0 deletions map.py
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 added playback/foo.avi
Binary file not shown.
3 changes: 3 additions & 0 deletions player.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class Player:
def do_turn(self, map):
pass
29 changes: 29 additions & 0 deletions random_bot.py
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

0 comments on commit 7f614da

Please sign in to comment.