berlin-ai 0.0.29 → 0.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://www.rubygems.org"
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,56 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ berlin-ai (0.0.29)
5
+ rainbow (~> 1.1.4)
6
+ sinatra (= 1.4.3)
7
+ sinatra-contrib (= 1.4.1)
8
+ thin (= 1.5.1)
9
+ yajl-ruby (= 1.1.0)
10
+
11
+ GEM
12
+ remote: http://www.rubygems.org/
13
+ specs:
14
+ backports (3.3.3)
15
+ coderay (1.0.9)
16
+ daemons (1.1.9)
17
+ eventmachine (1.0.3)
18
+ method_source (0.8.2)
19
+ minitest (4.7.5)
20
+ multi_json (1.7.9)
21
+ pry (0.9.12.2)
22
+ coderay (~> 1.0.5)
23
+ method_source (~> 0.8)
24
+ slop (~> 3.4)
25
+ rack (1.5.2)
26
+ rack-protection (1.5.0)
27
+ rack
28
+ rack-test (0.6.2)
29
+ rack (>= 1.0)
30
+ rainbow (1.1.4)
31
+ sinatra (1.4.3)
32
+ rack (~> 1.4)
33
+ rack-protection (~> 1.4)
34
+ tilt (~> 1.3, >= 1.3.4)
35
+ sinatra-contrib (1.4.1)
36
+ backports (>= 2.0)
37
+ multi_json
38
+ rack-protection
39
+ rack-test
40
+ sinatra (~> 1.4.0)
41
+ tilt (~> 1.3)
42
+ slop (3.4.6)
43
+ thin (1.5.1)
44
+ daemons (>= 1.0.9)
45
+ eventmachine (>= 0.12.6)
46
+ rack (>= 1.0.0)
47
+ tilt (1.4.1)
48
+ yajl-ruby (1.1.0)
49
+
50
+ PLATFORMS
51
+ ruby
52
+
53
+ DEPENDENCIES
54
+ berlin-ai!
55
+ minitest
56
+ pry
data/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # Berlin-Ai
2
+
3
+ Berlin-Ai is a gem to quickly ramp up on the [Berlin](http://www.berlin-ai.com) game.
4
+
5
+ ## Usage
6
+
7
+ Create your AI in a simple `.rb` file:
8
+
9
+ ```ruby
10
+ require 'berlin-ai' # Require the berlin-ai library.
11
+
12
+ class Berlin::AI::Player
13
+ def on_turn(game) # Implement the on_turn method of Berlin::AI::Player.
14
+ # Do your magic here.
15
+
16
+ # Here's an AI that randomly moves soldiers from node to node.
17
+ game.map.controlled_nodes.each do |node|
18
+ node.adjacent_nodes.shuffle.each do |other_node|
19
+ rand(0..(node.available_soldiers))
20
+ game.add_move(node.id, other_node.id, soldiers)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ ```
26
+
27
+ You're now ready to host your AI locally or with [Heroku](https://devcenter.heroku.com/articles/rack). We use [Sinatra](http://www.sinatrarb.com) in this gem, so a simple `ruby your_ai_file.rb` will launch the web server.
28
+
29
+ To modify some defaults, you can execute `ruby your_ai_file.rb -h` for a list of available options.
30
+
31
+ ## API
32
+
33
+ ### game (Berlin::AI::Game)
34
+
35
+ Entry point in the API from `Berlin::AI::Player#on_turn`.
36
+
37
+ ```ruby
38
+ # General information on the game.
39
+ game.id # This game unique id.
40
+ game.number_of_players # Number of players in this game.
41
+ game.maximum_number_of_turns # Maximum number of turns for this game.
42
+ game.player_id # Your player id for this game.
43
+ game.time_limit_per_turn # Maximum number of time (ms) to make your moves, per turn.
44
+
45
+ # Information on the current turn.
46
+ game.current_turn # The current turn count.
47
+ game.turns_left # Remaining turns before the end of the game.
48
+
49
+ # Add a move to perform this turn by your AI.
50
+ # IMPORTANT: 'from' and 'to' must be node objects.
51
+ game.add_move(from, to, number_of_soldiers)
52
+
53
+ # List of moves to perform this turn by your AI.
54
+ game.moves # => [{ from: node_id, to: node_id, number_of_soldiers: int }]
55
+
56
+ # The game's map with its own set of useful methods.
57
+ game.map
58
+ ```
59
+
60
+ ### map (Berlin::AI::Map)
61
+
62
+ Helper methods to work with nodes.
63
+
64
+ ```ruby
65
+ map.directed? # Determine if a path from A to B is A -> B (directed) or A <-> B (not directed)
66
+ map.nodes # All the nodes.
67
+ map.owned_nodes # Owned nodes, including those with 0 soldiers.
68
+ map.enemy_nodes # Enemy nodes.
69
+ map.free_nodes # Nodes not controlled by anyone.
70
+ map.foreign_nodes # Nodes not owned (i.e. enemy nodes and free nodes).
71
+ map.controlled_nodes # Owned nodes, excluding those with 0 soldiers.
72
+ ```
73
+
74
+ ### node (Berlin::AI::Node)
75
+
76
+ Node objects obtained when querying the map.
77
+
78
+ ```ruby
79
+ node.id # Id of the node.
80
+ node.type # Type of node.
81
+ node.player_id # Owner of the node.
82
+ node.number_of_soldiers # Number of soldiers on the node.
83
+ node.incoming_soldiers # Owned soldiers coming to this node (result from add_move calls).
84
+ node.available_soldiers # Owned remaining soldiers on this node (result from add_move calls).
85
+ node.==(other) # Check if two nodes are the same.
86
+ node.adjacent?(other_node) # Check if two nodes are adjacents.
87
+ node.occupied? # Check if some soldiers are on the node.
88
+ node.owned? # Check if you own the node.
89
+ node.free? # Check if no one own the node.
90
+ node.owned_by?(player_id) # Check if the node is owned by a given player.
91
+ node.adjacent_nodes # Get a list of adjacent nodes.
92
+ node.adjacent_nodes_and_self # Get a list of adjacent nodes, including this node.
93
+ node.soldiers_per_turn # Spawned soldiers per turn if you own this node.
94
+ node.points # Given points by this node at the end of the game.
95
+ ```
96
+
97
+ ## Installation
98
+
99
+ Add this line to your application's Gemfile:
100
+
101
+ gem 'berlin-ai'
102
+
103
+ And then execute:
104
+
105
+ $ bundle
106
+
107
+ Or install it yourself as:
108
+
109
+ $ gem install berlin-ai
110
+
111
+ ## Contributing
112
+
113
+ 1. Fork it
114
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
115
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
116
+ 4. Push to the branch (`git push origin my-new-feature`)
117
+ 5. Create new Pull Request
data/berlin-ai.gemspec CHANGED
@@ -20,6 +20,9 @@ Gem::Specification.new do |s|
20
20
  s.add_dependency 'rainbow', '~>1.1.4'
21
21
  s.add_dependency 'thin', '1.5.1'
22
22
 
23
+ s.add_development_dependency 'minitest'
24
+ s.add_development_dependency 'pry'
25
+
23
26
  s.files = `git ls-files`.split("\n")
24
27
 
25
28
  s.require_paths = ['lib', 'test']
data/lib/ai/fake.rb CHANGED
@@ -4,6 +4,8 @@ module Berlin
4
4
  module Fake
5
5
 
6
6
  MAP_DEFINITION = {
7
+ "directed" => false,
8
+
7
9
  "types" => [
8
10
  {"name" => "node", "points" => 0, "soldiers_per_turn" => 0},
9
11
  {"name" => "city", "points" => 1, "soldiers_per_turn" => 1}
@@ -228,7 +230,14 @@ class Berlin::Fake::Game
228
230
  ai_info['player_id'] = ai_name
229
231
  ai_info['game_id'] = n
230
232
 
231
- Berlin::AI::Game.new(ai_info['game_id'], Berlin::Fake::MAP_DEFINITION, ai_info)
233
+ ai_game = Berlin::AI::Game.new
234
+ ai_game.id = ai_info['game_id']
235
+ ai_game.map = Berlin::AI::Map.parse(Berlin::Fake::MAP_DEFINITION.merge('player_id' => ai_info['player_id']))
236
+ ai_game.player_id = ai_info['player_id']
237
+ ai_game.time_limit_per_turn = ai_info['time_limit_per_turn']
238
+ ai_game.maximum_number_of_turns = ai_info['maximum_number_of_turns']
239
+ ai_game.number_of_players = ai_info['number_of_players']
240
+ ai_game
232
241
  end
233
242
 
234
243
  player_name = "Player"
@@ -239,7 +248,13 @@ class Berlin::Fake::Game
239
248
  node['player_id'] = player_name
240
249
  node['number_of_soldiers'] = 5
241
250
 
242
- @player_game = Berlin::AI::Game.new(player_info['game_id'], Berlin::Fake::MAP_DEFINITION, player_info)
251
+ @player_game = Berlin::AI::Game.new
252
+ @player_game.id = player_info['game_id']
253
+ @player_game.map = Berlin::AI::Map.parse(Berlin::Fake::MAP_DEFINITION.merge('player_id' => player_info['player_id']))
254
+ @player_game.player_id = player_info['player_id']
255
+ @player_game.time_limit_per_turn = player_info['time_limit_per_turn']
256
+ @player_game.maximum_number_of_turns = player_info['maximum_number_of_turns']
257
+ @player_game.number_of_players = player_info['number_of_players']
243
258
 
244
259
  @state = Berlin::Fake::State.new(Berlin::Fake::GAME_STATE.dup)
245
260
  end
@@ -291,15 +306,13 @@ class Berlin::Fake::Game
291
306
  end
292
307
 
293
308
  def generate_moves
294
- info = {'current_turn' => @turn}
295
-
296
309
  @ai_games.each do |game|
297
310
  game.reset!
298
- game.update(info, @state.as_json)
311
+ game.update(@turn, @state.as_json)
299
312
  Berlin::Fake::Random.on_turn(game)
300
313
  end
301
314
 
302
- @player_game.update(info, @state.as_json)
315
+ @player_game.update(@turn, @state.as_json)
303
316
  @player_game.reset!
304
317
  Berlin::AI::Player.on_turn(@player_game)
305
318
  end
data/lib/ai/game.rb CHANGED
@@ -2,56 +2,13 @@ module Berlin
2
2
  module AI
3
3
  # Game keeps track of current games played by the server, indexing them on their uniq id.
4
4
  class Game
5
- attr_reader :id, :map, :moves, :player_id, :current_turn, :number_of_players, :time_limit_per_turn, :maximum_number_of_turns, :turns_left
6
-
7
- # Keep track of all current games
8
- @@games = {}
9
-
10
- def self.create_or_update action, infos, map, state
11
- # Check for params and quit on errors
12
- return if action.nil? || infos.nil? || map.nil? || state.nil?
13
-
14
- # First, we parse the received request
15
- infos = JSON.parse( infos )
16
- map = JSON.parse( map )
17
- state = JSON.parse( state )
18
-
19
- # Game id, set with player_id as well so an AI can fight with himself
20
- game_id = "#{infos['game_id']}-#{infos['player_id']}"
21
-
22
- # Then, let's see if we can find that game. If not, register it.
23
- if action == "ping"
24
- game = Berlin::AI::Game.new game_id, map, infos
25
- else
26
- game = (@@games[game_id] ||= Berlin::AI::Game.new( game_id, map, infos ))
27
- end
28
-
29
- if action == "game_over"
30
- # Release the game to avoid memory leaks
31
- @@games.delete game_id
32
- elsif infos && state
33
- # Now, we want to update the current state of the game with the new content, as well as other infos
34
- game.update infos, state
35
- end
5
+ include Internal
36
6
 
37
- game
38
- end
7
+ attr_accessor :id, :map, :moves, :player_id, :current_turn, :number_of_players,
8
+ :time_limit_per_turn, :maximum_number_of_turns, :turns_left
39
9
 
40
- def initialize id, map, infos
41
- @id = id
42
-
43
- # Extract usefull static informations
44
- @player_id = infos['player_id']
45
- @time_limit_per_turn = infos['time_limit_per_turn']
46
- @maximum_number_of_turns = infos['maximum_number_of_turns'].to_i
47
- @number_of_players = infos['number_of_players']
48
-
49
- # Create the map
50
- @map = Berlin::AI::Map.new map, infos
51
- end
52
-
53
- def add_move from, to, number_of_soldiers
54
- # remove moving soldiers from from node
10
+ def add_move(from, to, number_of_soldiers)
11
+ # remove moving soldiers from from node
55
12
  from.available_soldiers -= number_of_soldiers
56
13
 
57
14
  # adding incoming soldiers to next node
@@ -61,19 +18,6 @@ module Berlin
61
18
  @moves << {:from => from.to_i, :to => to.to_i, :number_of_soldiers => number_of_soldiers.to_i}
62
19
  end
63
20
 
64
- def update infos, state
65
- # Update turn infos
66
- @current_turn = infos['current_turn'].to_i
67
- @turns_left = @maximum_number_of_turns - @current_turn
68
-
69
- # Update map state
70
- @map.update state
71
- end
72
-
73
- def reset!
74
- @moves = []
75
- @map.nodes.each(&:reset!)
76
- end
77
21
  end
78
22
  end
79
23
  end
@@ -0,0 +1,65 @@
1
+ module Berlin
2
+ module AI
3
+ class Game
4
+ module Internal
5
+ module ClassMethods
6
+ # Keep track of all current games
7
+ @@games = {}
8
+
9
+ def create_or_update(action, infos, map, state)
10
+ # Check for params and quit on errors
11
+ return if action.nil? || infos.nil? || map.nil? || state.nil?
12
+
13
+ # First, we parse the received request
14
+ infos = JSON.parse( infos )
15
+ map = JSON.parse( map )
16
+ state = JSON.parse( state )
17
+
18
+ # Game id, set with player_id as well so an AI can fight with himself
19
+ game_id = "#{infos['game_id']}-#{infos['player_id']}"
20
+
21
+ # Then, let's see if we can find that game. If not, register it.
22
+ game = @@games[game_id] ||= begin
23
+ game = Berlin::AI::Game.new
24
+ game.id = game_id
25
+ game.map = Berlin::AI::Map.parse(map.merge(infos.select{ |k,v| ['directed', 'player_id'].include?(k) }))
26
+ game.player_id = infos['player_id']
27
+ game.time_limit_per_turn = infos['time_limit_per_turn']
28
+ game.maximum_number_of_turns = infos['maximum_number_of_turns']
29
+ game.number_of_players = infos['number_of_players']
30
+ game
31
+ end
32
+
33
+ if action == "game_over"
34
+ # Release the game to avoid memory leaks
35
+ @@games.delete(game_id)
36
+ elsif infos && state
37
+ # Now, we want to update the current state of the game with the new content, as well as other infos
38
+ game.update(infos['current_turn'], state)
39
+ end
40
+
41
+ game
42
+ end
43
+ end
44
+
45
+ def self.included(base)
46
+ base.extend(ClassMethods)
47
+ end
48
+
49
+ def update(current_turn, state)
50
+ # Update turn infos
51
+ @current_turn = current_turn.to_i
52
+ @turns_left = @maximum_number_of_turns - @current_turn
53
+
54
+ # Update map state
55
+ @map.update(state)
56
+ end
57
+
58
+ def reset!
59
+ @moves = []
60
+ @map.nodes.each(&:reset!)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
data/lib/ai/map.rb CHANGED
@@ -4,39 +4,13 @@ module Berlin
4
4
  # nodes, points, soldiers, etc. Game will then be able to pick any information
5
5
  # it wants from map to decide what are the best moves to do.
6
6
  class Map
7
- def initialize map, infos
8
- @player_id = infos['player_id']
9
- @nodes = {}
10
- @types = {}
11
- @directed = infos['directed'] || false
12
-
13
- # Node types
14
- map['types'].each do |type|
15
- @types[type['name']] = type
16
- end
17
-
18
- # Let's parse map['nodes'] and register all nodes we can find.
19
- # We'll keep track of them in @nodes so we can find them later.
20
- # At this step (Map creation...), we still don't know who possess
21
- # the node and how many soldiers there is. We'll get back to that later.
22
- # map['nodes'] => [{:id => STRING}, ...]
23
- map['nodes'].each do |node|
24
- @nodes[node['id']] = Berlin::AI::Node.new node, @types[node['type']]
25
- end
7
+ include Internal
26
8
 
27
- # Same thing here, with paths.
28
- # map['paths'] => [{:from => INTEGER, :to => INTEGER}, ...]
29
- map['paths'].each do |path|
30
- @nodes[path['from']].link_to @nodes[path['to']]
31
-
32
- # Don't forget! If the map is not directed, we must create the reverse link!
33
- @nodes[path['to']].link_to @nodes[path['from']] unless directed?
34
- end
35
- end
9
+ attr_accessor :player_id, :nodes_hash, :directed
36
10
 
37
11
  # Returns an array of all nodes of the map
38
12
  def nodes
39
- @nodes.values
13
+ @nodes_hash.values
40
14
  end
41
15
 
42
16
  # Returns an array of all owned nodes
@@ -78,18 +52,6 @@ module Berlin
78
52
  def directed?
79
53
  @directed
80
54
  end
81
-
82
- # Let's update the current state with the latest provided info! With this step,
83
- # we'll now know who possess the node and how many soldiers there is.
84
- # state contains an array of nodes, so we just have to loop on it.
85
- # state => [{:node_id => STRING, :number_of_soldiers => INTEGER, :player_id => INTEGER}, ...]
86
- def update state
87
- state.each do |n|
88
- node = @nodes[n['node_id']]
89
- node.number_of_soldiers = n['number_of_soldiers']
90
- node.player_id = n['player_id']
91
- end
92
- end
93
55
  end
94
56
  end
95
57
  end
@@ -0,0 +1,67 @@
1
+ module Berlin
2
+ module AI
3
+ class Map
4
+ module Internal
5
+ module ClassMethods
6
+ def parse(data)
7
+ map = Map.new
8
+
9
+ # Map details
10
+ map.directed = data['directed']
11
+ map.player_id = data['player_id']
12
+
13
+ # Node types
14
+ types = data['types'].each.with_object({}) do |type, types|
15
+ types[type['name']] = type
16
+ end
17
+
18
+ # Let's parse data['nodes'] and register all nodes we can find.
19
+ # We'll keep track of them in @nodes so we can find them later.
20
+ # At this step (Map creation...), we still don't know who possess
21
+ # the node and how many soldiers there is. We'll get back to that later.
22
+ # map['nodes'] => [{:id => STRING}, ...]
23
+ data['nodes'].each do |node|
24
+ map.nodes_hash[node['id']] = Berlin::AI::Node.parse(node.merge(types[node['type']]))
25
+ end
26
+
27
+ # Same thing here, with paths.
28
+ # map['paths'] => [{:from => INTEGER, :to => INTEGER}, ...]
29
+ data['paths'].each do |path|
30
+ map.nodes_hash[path['from']].link_to(map.nodes_hash[path['to']])
31
+
32
+ # Don't forget! If the map is not directed, we must create the reverse link!
33
+ map.nodes_hash[path['to']].link_to(map.nodes_hash[path['from']]) unless map.directed?
34
+ end
35
+
36
+ # and... return the newly created map
37
+ map
38
+ end
39
+ end
40
+
41
+ def self.included(base)
42
+ base.extend(ClassMethods)
43
+ end
44
+
45
+ def initialize(options={})
46
+ @nodes_hash = {}
47
+
48
+ options.each do |k,v|
49
+ self.send("#{k}=", v)
50
+ end
51
+ end
52
+
53
+ # Let's update the current state with the latest provided info! With this step,
54
+ # we'll now know who possess the node and how many soldiers there is.
55
+ # state contains an array of nodes, so we just have to loop on it.
56
+ # state => [{:node_id => STRING, :number_of_soldiers => INTEGER, :player_id => INTEGER}, ...]
57
+ def update(state)
58
+ state.each do |n|
59
+ node = @nodes_hash[n['node_id']]
60
+ node.number_of_soldiers = n['number_of_soldiers']
61
+ node.player_id = n['player_id']
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
data/lib/ai/node.rb CHANGED
@@ -4,39 +4,10 @@ module Berlin
4
4
  # We'll be able to use it in order to know if two
5
5
  # nodes are adjacent, how much points worth a node, etc.
6
6
  class Node
7
- attr_accessor :id, :player_id, :number_of_soldiers, :incoming_soldiers, :available_soldiers, :type
8
- attr_reader :soldiers_per_turn, :points
7
+ include Internal
9
8
 
10
- def initialize node, type
11
- @id = node['id']
12
- @type = node['type']
13
- @points = type['points']
14
- @soldiers_per_turn = type['soldiers_per_turn']
15
- @number_of_soldiers = 0
16
- @player_id = nil
17
- @links = []
18
- end
19
-
20
- # Reset information for new turn
21
- def reset!
22
- self.incoming_soldiers = 0
23
- self.available_soldiers = self.number_of_soldiers
24
- end
25
-
26
- # Somewhat useful
27
- def to_i
28
- @id.to_i
29
- end
30
-
31
- # Used to compare if two nodes are the same
32
- def ==(other)
33
- other.id == @id
34
- end
35
-
36
- # Registers a given node as an adjacent one.
37
- def link_to(other_node)
38
- @links << other_node
39
- end
9
+ attr_accessor :id, :player_id, :number_of_soldiers, :incoming_soldiers,
10
+ :available_soldiers, :type, :soldiers_per_turn, :points
40
11
 
41
12
  # Returns true if other_node is adjacent to self
42
13
  def adjacent?(other_node)
@@ -0,0 +1,59 @@
1
+ module Berlin
2
+ module AI
3
+ class Node
4
+ module Internal
5
+ module ClassMethods
6
+ def parse(data)
7
+ node = Node.new
8
+
9
+ node.id = data['id']
10
+ node.type = data['type']
11
+ node.points = data['points']
12
+ node.soldiers_per_turn = data['soldiers_per_turn']
13
+
14
+ node
15
+ end
16
+ end
17
+
18
+ def self.included(base)
19
+ base.extend(ClassMethods)
20
+ end
21
+
22
+ def initialize(options={})
23
+ @number_of_soldiers = 0
24
+ @player_id = nil
25
+ @links = []
26
+
27
+ options.each do |k,v|
28
+ self.send("#{k}=", v)
29
+ end
30
+ end
31
+
32
+ def to_s
33
+ "<Berlin::AI::Node @id=#{@id} @player_id='#{@player_id}' @type='#{@type}' @points=#{@points} @soldiers_per_turn=#{@soldiers_per_turn} @number_of_soldiers=#{@number_of_soldiers} @incoming_soldiers=#{@incoming_soldiers} @available_soldiers=#{@available_soldiers} @adjacent_nodes=#{adjacent_nodes.map(&:id)}>"
34
+ end
35
+
36
+ # Reset information for new turn
37
+ def reset!
38
+ self.incoming_soldiers = 0
39
+ self.available_soldiers = self.number_of_soldiers
40
+ end
41
+
42
+ # Somewhat useful
43
+ def to_i
44
+ @id.to_i
45
+ end
46
+
47
+ # Used to compare if two nodes are the same
48
+ def ==(other)
49
+ other.id == @id
50
+ end
51
+
52
+ # Registers a given node as an adjacent one.
53
+ def link_to(other_node)
54
+ @links << other_node
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
data/lib/berlin-ai.rb CHANGED
@@ -9,7 +9,7 @@ puts "| _ || -__|| _|| | || | | |_| |_ "
9
9
  puts "|_____||_____||__| |__|__||__|__| |___|___|_______|"
10
10
  puts
11
11
 
12
- %w(game map node fake).each do |file|
12
+ %w(game_internal game map_internal map node_internal node fake).each do |file|
13
13
  require File.expand_path(File.dirname( __FILE__ )) + "/ai/#{file}"
14
14
  end
15
15
 
data/lib/version.rb CHANGED
@@ -2,7 +2,7 @@ module Berlin
2
2
  module AI
3
3
  MAJOR = 0
4
4
  MINOR = 0
5
- BUILD = 29
5
+ BUILD = 32
6
6
 
7
7
  VERSION = "#{MAJOR}.#{MINOR}.#{BUILD}"
8
8
  end
data/test/map_test.rb ADDED
@@ -0,0 +1,35 @@
1
+
2
+ require_relative '../lib/ai/node_internal'
3
+ require_relative '../lib/ai/node'
4
+ require_relative '../lib/ai/map_internal'
5
+ require_relative '../lib/ai/map'
6
+
7
+ require "test/unit"
8
+
9
+ class MapTest < Test::Unit::TestCase
10
+
11
+ def setup
12
+ @node1 = Berlin::AI::Node.new(:id => 1, :player_id => 1)
13
+ @node2 = Berlin::AI::Node.new(:id => 2, :player_id => 1)
14
+ @node3 = Berlin::AI::Node.new(:id => 3, :player_id => nil)
15
+ @node4 = Berlin::AI::Node.new(:id => 4, :player_id => 2)
16
+ @node5 = Berlin::AI::Node.new(:id => 5, :player_id => 3)
17
+
18
+ @map = Berlin::AI::Map.new
19
+ @map.player_id = 1
20
+ @map.nodes = {1 => @node1, 2 => @node2, 3 => @node3, 4 => @node4, 5 => @node5}
21
+ end
22
+
23
+ def test_nodes_returns_an_array_of_all_nodes
24
+ assert_equal [@node1, @node2, @node3, @node4, @node5], @map.nodes
25
+ end
26
+
27
+ def test_owned_nodes_returns_an_array_of_owned_nodes_for_current_player
28
+ assert_equal [@node1, @node2], @map.owned_nodes
29
+ end
30
+
31
+ def test_foreign_nodes_returns_and_array_of_nodes_that_the_current_player_does_not_owned
32
+ assert_equal [@node3, @node4, @node5], @map.foreign_nodes
33
+ end
34
+
35
+ end
data/test/node_test.rb ADDED
@@ -0,0 +1,77 @@
1
+ require_relative '../lib/ai/node_internal'
2
+ require_relative '../lib/ai/node'
3
+
4
+ require "test/unit"
5
+
6
+ class NodeTest < Test::Unit::TestCase
7
+
8
+ def test_equals_returns_true_if_two_nodes_have_the_same_id
9
+ node1 = Berlin::AI::Node.new(:id => 1)
10
+ node2 = Berlin::AI::Node.new(:id => 2)
11
+
12
+ assert node1 != node2
13
+
14
+ node2.id = 1
15
+
16
+ assert node1 == node2
17
+ end
18
+
19
+ def test_reset_resets_all_turn_relative_data
20
+ node = Berlin::AI::Node.new(:number_of_soldiers => 2, :incoming_soldiers => 3, :available_soldiers => 4)
21
+ node.reset!
22
+ assert_equal 0, node.incoming_soldiers
23
+ assert_equal node.number_of_soldiers, node.available_soldiers
24
+ end
25
+
26
+ def test_adjacent_returns_weither_or_not_a_node_is_adjacent
27
+ node1 = Berlin::AI::Node.new(:id => 1)
28
+ node2 = Berlin::AI::Node.new(:id => 2)
29
+ node3 = Berlin::AI::Node.new(:id => 3)
30
+
31
+ node1.link_to(node2)
32
+
33
+ assert node1.adjacent?(node2)
34
+ assert !node1.adjacent?(node3)
35
+ end
36
+
37
+ def test_occupied_returns_true_if_the_node_has_at_least_one_soldiers
38
+ node = Berlin::AI::Node.new
39
+
40
+ assert !node.occupied?
41
+
42
+ node.number_of_soldiers = 10
43
+
44
+ assert node.occupied?
45
+ end
46
+
47
+ def test_free_returns_true_if_the_node_is_not_owned_by_any_player
48
+ node = Berlin::AI::Node.new
49
+
50
+ assert node.free?
51
+
52
+ node.player_id = 1
53
+
54
+ assert !node.free?
55
+ end
56
+
57
+ def test_owned_by_returns_true_if_owned_by_the_provided_player
58
+ node = Berlin::AI::Node.new
59
+
60
+ assert !node.owned_by?(1)
61
+
62
+ node.player_id = 1
63
+
64
+ assert node.owned_by?(1)
65
+ end
66
+
67
+ def test_owned_returns_true_if_owned_by_a_player
68
+ node = Berlin::AI::Node.new
69
+
70
+ assert !node.owned?
71
+
72
+ node.player_id = 1
73
+
74
+ assert node.owned?
75
+ end
76
+
77
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: berlin-ai
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.29
4
+ version: 0.0.32
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2013-08-28 00:00:00.000000000 Z
14
+ date: 2013-09-13 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: sinatra
@@ -93,6 +93,38 @@ dependencies:
93
93
  - - '='
94
94
  - !ruby/object:Gem::Version
95
95
  version: 1.5.1
96
+ - !ruby/object:Gem::Dependency
97
+ name: minitest
98
+ requirement: !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ! '>='
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: pry
114
+ requirement: !ruby/object:Gem::Requirement
115
+ none: false
116
+ requirements:
117
+ - - ! '>='
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ type: :development
121
+ prerelease: false
122
+ version_requirements: !ruby/object:Gem::Requirement
123
+ none: false
124
+ requirements:
125
+ - - ! '>='
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
96
128
  description: Berlin Artificial Intelligence
97
129
  email:
98
130
  - christ.blais@gmail.com
@@ -102,16 +134,23 @@ executables: []
102
134
  extensions: []
103
135
  extra_rdoc_files: []
104
136
  files:
137
+ - Gemfile
138
+ - Gemfile.lock
105
139
  - LICENSE
106
- - README
140
+ - README.md
107
141
  - berlin-ai.gemspec
108
142
  - lib/ai/fake.rb
109
143
  - lib/ai/game.rb
144
+ - lib/ai/game_internal.rb
110
145
  - lib/ai/map.rb
146
+ - lib/ai/map_internal.rb
111
147
  - lib/ai/node.rb
148
+ - lib/ai/node_internal.rb
112
149
  - lib/ai/player.rb
113
150
  - lib/berlin-ai.rb
114
151
  - lib/version.rb
152
+ - test/map_test.rb
153
+ - test/node_test.rb
115
154
  - test/test_ai.rb
116
155
  homepage: http://github.com/thirdside/berlin-ai
117
156
  licenses: []
data/README DELETED
File without changes