Skip to content

Commit

Permalink
Woo
Browse files Browse the repository at this point in the history
  • Loading branch information
Harry Brundage committed Sep 13, 2010
1 parent 077abe4 commit 5b3a3b2
Show file tree
Hide file tree
Showing 25 changed files with 557 additions and 76,416 deletions.
10 changes: 7 additions & 3 deletions .gitignore
Expand Up @@ -27,11 +27,12 @@ pkg
#
# For MacOS:
#
#.DS_Store
.DS_Store
#
# For TextMate
#*.tmproj
#tmtags
.tmproj
tmtags

#
# For emacs:
#*~
Expand All @@ -40,3 +41,6 @@ pkg
#
# For vim:
#*.swp

# Finchbot
logs
7 changes: 6 additions & 1 deletion Rakefile
@@ -1,5 +1,7 @@
require 'rubygems'
require 'bundler'
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))

begin
Bundler.setup(:default, :development)
rescue Bundler::BundlerError => e
Expand Down Expand Up @@ -38,8 +40,11 @@ end

task :default => :spec

require 'finchbot'
require 'darwin'

task :build_code do
files = Dir.glob("lib/finchbot*") + Dir.glob("lib/planetwars*") + ["mybot.rb"]
files = Dir.glob("lib/finchbot*") + Dir.glob("lib/finchbot/**/*") + Dir.glob("lib/planetwars*") + ["mybot.rb", "parameters.yml"]
puts "Zipping..."
system "zip package"+Time.now.to_i.to_s+".zip "+files.join(" ")
end
Expand Down
10 changes: 10 additions & 0 deletions lib/darwin.rb
@@ -0,0 +1,10 @@
require 'ai4r'
require 'darwin/chromosome'
require 'darwin/game_player'

task :evolve do
search = Ai4r::GeneticAlgorithm::GeneticSearch.new(2, 3)
result = search.run
puts result.inspect
puts result.data
end
92 changes: 92 additions & 0 deletions lib/darwin/chromosome.rb
@@ -0,0 +1,92 @@
module Finch
class Chromosome

attr_accessor :data
attr_accessor :normalized_fitness
attr_accessor :fitness

def initialize(data)
@data = data
end

# The fitness method quantifies the optimality of a solution
# (that is, a chromosome) in a genetic algorithm so that that particular
# chromosome may be ranked against all the other chromosomes.
#
# Optimal chromosomes, or at least chromosomes which are more optimal,
# are allowed to breed and mix their datasets by any of several techniques,
# producing a new generation that will (hopefully) be even better.
def fitness
return @fitness if @fitness
maps = Array.new(99) do |i|
"maps/map#{i+1}.txt"
end

bots = ["RandomBot", "BullyBot", "DualBot", "ProspectorBot", "RageBot"].collect do |s|
"java -jar example_bots/#{s}.jar"
end
finch = "/Users/hornairs/.rvm/rubies/ruby-1.9.2-rc2/bin/ruby mybot.rb #{self.data.join(" ")}"
scores = []
maps[0..2].each do |map|
bots[1..3].each do |bot|
scores.push GamePlayer.new.play(bot, finch, map)
end
end

@fitness = scores.sum / scores.length
return @fitness
end

def self.mutate(chromosome)
if chromosome.normalized_fitness && rand < ((1 - chromosome.normalized_fitness) * 0.3)
data = chromosome.data
index = rand(data.length-1)
data[index] = data[index] + ((rand - 0.5) * data[index])
chromosome.data = data
chromosome.fitness = nil
end
end

def self.reproduce(a, b)
data_size = Finch.parameter_count-2
crossover = rand(data_size)
spawn = a.data[0..crossover] + b.data[crossover+1..-1]
return self.class.new(spawn)
end

# Initializes an individual solution (chromosome) for the initial
# population. Usually the chromosome is generated randomly, but you can
# use some problem domain knowledge, to generate a
# (probably) better initial solution.
def self.seed
seed = Array.new(Finch.parameter_count) do |i|
rand
end
return Chromosome.new(seed)
end
end
end

module Ai4r
module GeneticAlgorithm
class GeneticSearch
def generate_initial_population
@population = []
@population_size.times do
population << Finch::Chromosome.seed
end
end

def reproduction(selected_to_breed)
offsprings = []
0.upto(selected_to_breed.length/2-1) do |i|
offsprings << Finch::Chromosome.reproduce(selected_to_breed[2*i], selected_to_breed[2*i+1])
end
@population.each do |individual|
Finch::Chromosome.mutate(individual)
end
return offsprings
end
end
end
end
30 changes: 30 additions & 0 deletions lib/darwin/game_player.rb
@@ -0,0 +1,30 @@
require "open3"
module Finch
class GamePlayer
def play(bot1, bot2, map)
str = "java -jar tools/PlayGame.jar #{map} 1000 1000 logs/log#{Time.now.to_i}.txt \"#{bot1}\" \"#{bot2}\""
puts str
output = ""
Open3.popen3(str) { |stdin, stdout, stderr|
output = stdout.read
errors = stderr.read
puts errors
}
self.parse_output(output)
end

def parse_output(string)
if string.match("WARNING: player 2 timed out")
return -1001
else
turns = string.scan(/Turn \d{1,3}/).length
if string.match("Player 2 Wins!")
winner = 1
else
winner = -1
end
return (1000 - turns) * winner
end
end
end
end
33 changes: 24 additions & 9 deletions lib/finchbot.rb
Expand Up @@ -12,35 +12,50 @@ def angles
def lookahead
@lookahead || 5
end

def parameter_count
6
end
end

class Bot
attr_reader :parameters

def initialize(parameters)
@parameters = decode_parameters(parameters)
@parameters = decode_parameters(parameters) if parameters.is_a?(Array)
@parameters = parameters if parameters.is_a?(Hash)
end

def decode_parameters(ps)
r = {:angle_weights => {:investment => 2, :attackable => 1, :defense_assist => 2}, :score_threshhold => 1}
ps = ps.map(&:to_f)
r = {:angle_weights => {:investment => ps[1], :attackable => ps[2], :defendable => ps[3], :defense_assist => ps[4], :crippling => ps[5]}, :score_threshold => ps[0]}
r[:total_weight] = r[:angle_weights].values.sum
r
end

def angled_strategy

moves = []
Finch.pw.my_planets.each do |src|
Finch.pw.planets.each do |dest|
opinions = []
Finch.angles.map(&:new).each do |a|
opinions.push((a.opinion(src, dest) || 0) * self.parameters[:angle_weights][a.class.key])
reservation = 0
Finch.angles.collect{|a| a.new(src,dest) }.each do |a|
opinions.push((a.opinion || 0) * self.parameters[:angle_weights][a.class.key])
reservation += a.reservation
end
puts "comparing #{src.id} and #{dest.id}, gives #{opinions.join(" ")}"
# puts "comparing #{src.id} and #{dest.id}, gives #{opinions.join(" ")} => #{opinions.sum}"
opinion = opinions.sum
if(opinion > self.parameters[:score_threshhold])
num_ships = src.num_ships / 2
Finch.pw.issue_order(src, dest, num_ships)
if(opinion > self.parameters[:score_threshold])
moves.push({:source => src, :destination => dest, :score => opinion, :reservation => reservation})
end
end
moves.sort_by {|m| m[:score]}
moves.each do |move|
ships = move[:reservation]
if move[:source].num_ships < move[:reservation]
ships = move[:source].num_ships - 1
end
Finch.pw.issue_order(move[:source], move[:destination], ships) if ships > 0
end
end
end
Expand Down
32 changes: 26 additions & 6 deletions lib/finchbot/angles.rb
Expand Up @@ -2,18 +2,38 @@

module Finch
class Angle
def self.inherited(subclass)
Finch.angles += [subclass]
end
def self.key
self.name.to_s[7..-6].underscore.to_sym
class << self
def inherited(subclass)
Finch.angles += [subclass]
end
def key
self.name.to_s[7..-6].underscore.to_sym
end
end

attr_reader :source, :destination, :reservation

def initialize(source, destination)
@source, @destination = source, destination
end

def opinion(source, destination, state)
def opinion
0
end

def reservation
@reservation || 0
end

private

def reserve(num_ships)
@reservation = self.reservation + num_ships
end

def reserve_conquering_fleet!
reserve(@destination.num_ships)
end
end
end

Expand Down
6 changes: 3 additions & 3 deletions lib/finchbot/angles/attackable.rb
@@ -1,8 +1,8 @@
module Finch
class AttackableAngle < Angle
def opinion(source, destination)
if destination.owner != $FINCH && source.num_ships > 10
1
def opinion
if destination.owner != $FINCH
Finch.pw.distance(source, destination) / (destination.num_ships + 1)
else
0
end
Expand Down
11 changes: 11 additions & 0 deletions lib/finchbot/angles/crippling.rb
@@ -0,0 +1,11 @@
module Finch
class CripplingAngle < Angle
def opinion
if destination.owner == $ENEMY
destination.num_ships / Finch.pw.distance(source, destination)
else
0
end
end
end
end
7 changes: 7 additions & 0 deletions lib/finchbot/angles/defendable.rb
@@ -0,0 +1,7 @@
module Finch
class DefendableAngle < Angle
def opinion
source.num_ships - destination.num_ships
end
end
end
2 changes: 1 addition & 1 deletion lib/finchbot/angles/defense_assist.rb
@@ -1,7 +1,7 @@

module Finch
class DefenseAssistAngle < Angle
def opinion(source, destination)
def opinion
return 0 if destination.owner != $FINCH

attackers = destination.incoming_fleets.inject(Array.new(Finch.lookahead, 0)) do |acc, fleet|
Expand Down
18 changes: 13 additions & 5 deletions lib/finchbot/angles/investment.rb
@@ -1,19 +1,27 @@
module Finch
class InvestmentAngle < Angle
def find_best_investments
def self.calculate_investments
calculations = []
Finch.pw.my_planets.each do |src|
calculations[src.id] = []
Finch.pw.not_my_planets.each do |dest|
# How good is the growth rate in comparison to how many ships would be generated while not doing so
calculations[src.id][dest.id] = dest.growth_rate / (src.growth_rate * Finch.pw.distance(src, dest))
# calculations[src.id][dest.id] = (dest.growth_rate * 10) / ((src.growth_rate * Finch.pw.distance(src, dest)))
calculations[src.id][dest.id] = dest.growth_rate / (src.growth_rate + 1)
end
end
calculations
end
def opinion(source, destination)
@calculations ||= self.find_best_investments
@calculations[source.id][destination.id] || 0

def opinion
Finch.pw.calculations[:investments] ||= self.class.calculate_investments
calcs = Finch.pw.calculations[:investments]
if(calcs[source.id][destination.id])
reserve_conquering_fleet!
calcs[source.id][destination.id]
else
0
end
end
end
end
3 changes: 2 additions & 1 deletion lib/planetwars/planetwars.rb
Expand Up @@ -43,8 +43,9 @@ def incoming_fleets
end

class PlanetWars
attr_reader :planets, :fleets
attr_reader :planets, :fleets, :calculations
def initialize(game_state)
@calculations = {}
parse_game_state(game_state)
end

Expand Down

0 comments on commit 5b3a3b2

Please sign in to comment.