berlin-ai 0.0.29 → 0.0.32

Sign up to get free protection for your applications and to get access to all the features.
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