public
Description: Player for the Ruby Sparring Battleship tournament
Homepage: http://iannopollo.com
Clone URL: git://github.com/siannopollo/joshua_son_of_nun.git
joshua_son_of_nun / game_simulator.rb
100644 151 lines (125 sloc) 4.353 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
require 'init'
 
OLD_STRATEGIES = JoshuaSonOfNun::Strategy.strategies.dup
JoshuaSonOfNun::Strategy.module_eval do
  class << self
    attr_accessor :strategies
  end
end
JoshuaSonOfNun::Strategy.strategies = OLD_STRATEGIES
 
def Space(string)
  coordinates, orientation = string.split(' ')
  row, column = coordinates.scan(/(\w)(\d{1,2})/).first
  JoshuaSonOfNun::Space.new(row, column, orientation)
end
 
class GameSimulator
  attr_reader :current_player, :game_over, :opponent,
              :player_one_strategy, :player_two_strategy,
              :player_one, :player_two, :players, :results,
              :ship_placement, :ships
  
  alias_method :game_over?, :game_over
  
  def initialize(player_one_strategy, player_two_strategy)
    @player_one_strategy, @player_two_strategy = player_one_strategy, player_two_strategy
    @player_one = JoshuaSonOfNun::Player.new
    @player_two = JoshuaSonOfNun::Player.new
    tag_players!
    
    @moves = []
    @results = {@player_one.name => [], @player_two.name => []}
  end
  
  def determine_damage(target)
    hit, sunk = nil
    ship_placement[opponent.name].each do |ship, spaces|
      space = spaces.delete(target)
      if space
        hit = true
        sunk = spaces.empty?
        break
      end
    end
    @game_over = ship_placement[opponent.name].values.flatten.empty?
    [hit, sunk]
  end
  
  def fire_on_opponent
    target = current_player.next_target
    ship_hit, ship_sunk = determine_damage(target)
    
    current_player.target_result(target.to_s, ship_hit, ship_sunk)
    opponent.enemy_targeting(target.to_s)
  end
  
  def force_strategy(strategy)
    JoshuaSonOfNun::Strategy.strategies = [strategy]
  end
  
  def gather_ship_placement
    players.each do |player|
      placement = {}
      ships.each do |ship|
        placement[ship] = Space(player.send("#{ship}_placement")).spaces_for_placement(player.instance_variable_get("@#{ship}").length)
      end
      
      ship_placement[player.name] = placement
    end
  end
  
  def prepare_report
    @filename = File.dirname(__FILE__) + "/reports/#{strategy_name(@player_one)}_vs_#{strategy_name(@player_two)}.csv"
    `test -f #{@filename} && echo '' || echo '#{strategy_name(@player_one)},#{strategy_name(@player_two)},moves' > #{@filename}`
  end
  
  def report_results
    result = (@current_player == @player_one ? '1,0,' : '0,1,') + (@moves.last/2.0).ceil.to_s
    `echo '#{result}' >> #{@filename}`
    print "\e[32m.\e[0m"; STDOUT.flush
  end
  
  def reset
    @players = [@player_one, @player_two]
    
    force_strategy(player_one_strategy) unless player_one_strategy.nil?
    @player_one.new_game(@player_two.name)
    force_strategy(player_two_strategy) unless player_two_strategy.nil?
    @player_two.new_game(@player_one.name)
    prepare_report
    
    @ship_placement = {}
    @ships = [:battleship, :carrier, :destroyer, :patrolship, :submarine]
    gather_ship_placement
    
    @moves << 0
    @game_over = false
  end
  
  def run!
    reset
    
    until game_over?
      switch_turns
      fire_on_opponent
    end
    
    @results[@current_player.name] << strategy_name(@current_player)
    report_results
  end
  
  def strategy_name(player)
    player.instance_variable_get('@strategy').class.name.split("::").last
  end
  
  def summarized_results
    summary = results.collect do |player, winning_strategies|
      output = "#{player}:"
      output << " #{winning_strategies.size} wins"
      strategy_count = OLD_STRATEGIES.uniq.collect do |strategy|
        count = winning_strategies.select {|s| s == strategy}.size
        "#{strategy} => #{count}"
      end
      output << " (#{strategy_count * ', '})"
    end.reverse * "\n"
    average_moves = @moves.inject(0) {|sum, n| sum += n} / (2 * @moves.size.to_f)
    summary << "\n#{average_moves} moves per game\n"
    summary
  end
  
  def switch_turns
    @current_player = @players.shift
    @players << @current_player
    @opponent = @players.first
    @moves << @moves.pop + 1
  end
  
  def tag_players!
    class << player_one
      def name; 'player_one'; end
    end
    class << player_two
      def name; 'player_two'; end
    end
  end
end
 
number_of_games = (ARGV.pop || 21).to_i
simulator = GameSimulator.new(ARGV.shift, ARGV.shift)
number_of_games.times {simulator.run!}
puts '', simulator.summarized_results, ''