software_challenge_client 0.1.5 → 0.2.0
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/.rubocop.yml +6 -0
- data/AUTHORS +6 -0
- data/Guardfile +44 -0
- data/README.md +45 -0
- data/RELEASES.md +4 -0
- data/develop.sh +3 -0
- data/example/client.rb +45 -17
- data/example/main.rb +1 -1
- data/generate-authors.sh +19 -0
- data/lib/software_challenge_client.rb +18 -15
- data/lib/software_challenge_client/action.rb +278 -0
- data/lib/software_challenge_client/board.rb +74 -289
- data/lib/software_challenge_client/client_interface.rb +8 -3
- data/lib/software_challenge_client/condition.rb +2 -4
- data/lib/software_challenge_client/debug_hint.rb +3 -25
- data/lib/software_challenge_client/direction.rb +39 -0
- data/lib/software_challenge_client/field.rb +34 -12
- data/lib/software_challenge_client/field_type.rb +29 -8
- data/lib/software_challenge_client/field_unavailable_exception.rb +17 -0
- data/lib/software_challenge_client/game_state.rb +88 -255
- data/lib/software_challenge_client/invalid_move_exception.rb +16 -0
- data/lib/software_challenge_client/logging.rb +24 -0
- data/lib/software_challenge_client/move.rb +36 -35
- data/lib/software_challenge_client/network.rb +16 -19
- data/lib/software_challenge_client/player.rb +47 -10
- data/lib/software_challenge_client/player_color.rb +8 -7
- data/lib/software_challenge_client/protocol.rb +131 -83
- data/lib/software_challenge_client/runner.rb +9 -7
- data/lib/software_challenge_client/version.rb +1 -1
- data/release.sh +9 -0
- data/software_challenge_client.gemspec +20 -8
- metadata +175 -7
- data/lib/software_challenge_client/connection.rb +0 -56
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'typesafe_enum'
|
4
|
+
# A direction on the hexagonal board of the game. Possible directions are:
|
5
|
+
# * RIGHT
|
6
|
+
# * UP_RIGHT
|
7
|
+
# * UP_LEFT
|
8
|
+
# * LEFT
|
9
|
+
# * DOWN_LEFT
|
10
|
+
# * DOWN_RIGHT
|
11
|
+
# Access them as Direction::RIGHT for example.
|
12
|
+
class Direction < TypesafeEnum::Base
|
13
|
+
new :RIGHT
|
14
|
+
new :UP_RIGHT
|
15
|
+
new :UP_LEFT
|
16
|
+
new :LEFT
|
17
|
+
new :DOWN_LEFT
|
18
|
+
new :DOWN_RIGHT
|
19
|
+
|
20
|
+
# @param direction [Direction] starting direction
|
21
|
+
# @param turns [Integer] number of turns (positive means turning
|
22
|
+
# counterclockwise).
|
23
|
+
# @return [Direction] The direction when turning the given number of steps
|
24
|
+
# from the given direction.
|
25
|
+
def self.get_turn_direction(direction, turns)
|
26
|
+
# order of directions is equal to counterclockwise turning
|
27
|
+
Direction.find_by_ord((direction.ord + turns) % 6)
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [Turn] The Turn action to get from from_direction to to_direction.
|
31
|
+
def self.from_to(from_direction, to_direction)
|
32
|
+
distance = (to_direction.ord - from_direction.ord + 6) % 6
|
33
|
+
if distance > 3
|
34
|
+
distance = distance - 6
|
35
|
+
end
|
36
|
+
Turn.new(distance)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -1,13 +1,8 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
require_relative 'player_color'
|
3
2
|
require_relative 'field_type'
|
4
3
|
|
5
|
-
#
|
6
|
-
# A field on the game board
|
4
|
+
# A field on the game board.
|
7
5
|
class Field
|
8
|
-
# @!attribute [rw] ownerColor
|
9
|
-
# @return [PlayerColor] the field's owner's color
|
10
|
-
attr_accessor :ownerColor
|
11
6
|
# @!attribute [rw] type
|
12
7
|
# @return [PlayerColor] the field's type
|
13
8
|
attr_accessor :type
|
@@ -18,26 +13,53 @@ class Field
|
|
18
13
|
# @return [Integer] the field's y-coordinate
|
19
14
|
attr_reader :y
|
20
15
|
|
16
|
+
# @!attribute [r] direction
|
17
|
+
# @return [Integer] the direction of the tile which the field belongs to
|
18
|
+
attr_reader :direction
|
19
|
+
|
20
|
+
# @!attribute [r] index
|
21
|
+
# @return [Integer] the index of the tile which the field belongs to
|
22
|
+
attr_reader :index
|
23
|
+
|
24
|
+
# @!attribute [r] points
|
25
|
+
# @return [Integer] the points awarded to a player placed on this field additionally to points for current tile and passengers
|
26
|
+
attr_reader :points
|
27
|
+
|
21
28
|
# Initializer
|
22
29
|
#
|
23
30
|
# @param type [FieldType] field type
|
24
31
|
# @param x [Integer] x-coordinate
|
25
32
|
# @param y [Integer] y-coordinate
|
26
|
-
|
27
|
-
|
33
|
+
# @param index [Integer] index of tile
|
34
|
+
# @param direction [Integer] direction of tile
|
35
|
+
# @param points [Integer] points
|
36
|
+
def initialize(type, x, y, index, direction, points)
|
28
37
|
self.type = type
|
29
38
|
@x = x
|
30
39
|
@y = y
|
40
|
+
@index = index
|
41
|
+
@direction = direction
|
42
|
+
@points = points
|
31
43
|
end
|
32
44
|
|
33
45
|
def ==(another_field)
|
34
|
-
return self.
|
35
|
-
self.type == another_field.type &&
|
46
|
+
return self.type == another_field.type &&
|
36
47
|
self.x == another_field.x &&
|
37
48
|
self.y == another_field.y
|
38
49
|
end
|
39
50
|
|
51
|
+
# @return [Boolean] true if the field may never be moved onto.
|
52
|
+
def blocked?
|
53
|
+
[FieldType::BLOCKED,
|
54
|
+
FieldType::PASSENGER0,
|
55
|
+
FieldType::PASSENGER1,
|
56
|
+
FieldType::PASSENGER2,
|
57
|
+
FieldType::PASSENGER3,
|
58
|
+
FieldType::PASSENGER4,
|
59
|
+
FieldType::PASSENGER5].include? type
|
60
|
+
end
|
61
|
+
|
40
62
|
def to_s
|
41
|
-
return "Field: x = #{self.x}, y = #{self.y},
|
63
|
+
return "Field: x = #{self.x}, y = #{self.y}, type = #{self.type}"
|
42
64
|
end
|
43
|
-
end
|
65
|
+
end
|
@@ -1,9 +1,30 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
2
|
+
|
3
|
+
require 'typesafe_enum'
|
4
|
+
# All possible field types:
|
5
|
+
# * WATER
|
6
|
+
# * BLOCKED
|
7
|
+
# * PASSENGER0
|
8
|
+
# * PASSENGER1
|
9
|
+
# * PASSENGER2
|
10
|
+
# * PASSENGER3
|
11
|
+
# * PASSENGER4
|
12
|
+
# * PASSENGER5
|
13
|
+
# * SANDBANK
|
14
|
+
# * LOG
|
15
|
+
# * GOAL
|
16
|
+
# Access them with FieldType::WATER.
|
17
|
+
class FieldType < TypesafeEnum::Base
|
18
|
+
new :WATER
|
19
|
+
new :BLOCKED
|
20
|
+
new :PASSENGER0
|
21
|
+
new :PASSENGER1
|
22
|
+
new :PASSENGER2
|
23
|
+
new :PASSENGER3
|
24
|
+
new :PASSENGER4
|
25
|
+
new :PASSENGER5
|
26
|
+
new :SANDBANK
|
27
|
+
new :LOG
|
28
|
+
new :GOAL
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# Exception indicating that a requested field does not exist.
|
3
|
+
class FieldUnavailableException < StandardError
|
4
|
+
# @!attribute [r] x
|
5
|
+
# @return [Integer] the X-coordinate of the requested field.
|
6
|
+
attr_reader :x
|
7
|
+
|
8
|
+
# @!attribute [r] y
|
9
|
+
# @return [Integer] the Y-coordinate of the requested field.
|
10
|
+
attr_reader :y
|
11
|
+
|
12
|
+
def initialize(x, y)
|
13
|
+
super("Field with coordinates (#{x},#{y}) is not available.")
|
14
|
+
@x = x
|
15
|
+
@y = y
|
16
|
+
end
|
17
|
+
end
|
@@ -1,25 +1,22 @@
|
|
1
|
-
# encoding:
|
1
|
+
# encoding: utf-8
|
2
2
|
require_relative './util/constants'
|
3
3
|
require_relative 'player'
|
4
4
|
require_relative 'board'
|
5
5
|
require_relative 'move'
|
6
6
|
require_relative 'condition'
|
7
|
-
require_relative 'player_color'
|
8
7
|
require_relative 'field_type'
|
9
8
|
|
10
|
-
#
|
11
|
-
# The state of a game
|
9
|
+
# The state of a game, as received from the server.
|
12
10
|
class GameState
|
13
|
-
|
14
11
|
# @!attribute [rw] turn
|
15
12
|
# @return [Integer] turn number
|
16
13
|
attr_accessor :turn
|
17
|
-
# @!attribute [rw]
|
14
|
+
# @!attribute [rw] start_player_color
|
18
15
|
# @return [PlayerColor] the start-player's color
|
19
|
-
attr_accessor :
|
20
|
-
# @!attribute [rw]
|
16
|
+
attr_accessor :start_player_color
|
17
|
+
# @!attribute [rw] current_player_color
|
21
18
|
# @return [PlayerColor] the current player's color
|
22
|
-
attr_accessor :
|
19
|
+
attr_accessor :current_player_color
|
23
20
|
# @!attribute [r] red
|
24
21
|
# @return [Player] the red player
|
25
22
|
attr_reader :red
|
@@ -35,310 +32,146 @@ class GameState
|
|
35
32
|
# @!attribute [rw] condition
|
36
33
|
# @return [Condition] the winner and winning reason
|
37
34
|
attr_accessor :condition
|
35
|
+
# @!attribute [rw] free_acceleration
|
36
|
+
# @return [Boolean] true if the free acceleration for this turn is still
|
37
|
+
# available.
|
38
|
+
attr_accessor :free_acceleration
|
39
|
+
alias free_acceleration? free_acceleration
|
40
|
+
|
41
|
+
# @!attribute [rw] free_turn
|
42
|
+
# @return [Boolean] True if the free turning for this turn is still
|
43
|
+
# available.
|
44
|
+
attr_accessor :free_turn
|
45
|
+
alias free_turn? free_turn
|
46
|
+
|
47
|
+
# @!attribute [rw] additional_free_turn_after_push
|
48
|
+
# @return [Boolean] True if the free turning for this turn is still
|
49
|
+
# available.
|
50
|
+
attr_accessor :additional_free_turn_after_push
|
51
|
+
alias additional_free_turn_after_push? additional_free_turn_after_push
|
52
|
+
|
53
|
+
POINTS_PER_TILE = 5
|
54
|
+
POINTS_PER_PASSENGER = 5
|
38
55
|
|
39
56
|
def initialize
|
40
|
-
|
41
|
-
|
42
|
-
|
57
|
+
@current_player_color = PlayerColor::RED
|
58
|
+
@start_player_color = PlayerColor::RED
|
59
|
+
@board = Board.new
|
60
|
+
@free_acceleration = true
|
61
|
+
@free_turn = true
|
62
|
+
@additional_free_turn_after_push = false
|
43
63
|
end
|
44
64
|
|
45
65
|
# adds a player to the gamestate
|
46
66
|
#
|
47
67
|
# @param player [Player] the player, that will be added
|
48
|
-
def
|
68
|
+
def add_player(player)
|
49
69
|
if player.color == PlayerColor::RED
|
50
70
|
@red = player
|
51
|
-
|
52
|
-
|
53
|
-
end
|
71
|
+
elsif player.color == PlayerColor::BLUE
|
72
|
+
@blue = player
|
54
73
|
end
|
55
74
|
end
|
56
75
|
|
57
76
|
# gets the current player
|
58
77
|
#
|
59
78
|
# @return [Player] the current player
|
60
|
-
def
|
61
|
-
if
|
62
|
-
|
63
|
-
else
|
64
|
-
return self.blue
|
79
|
+
def current_player
|
80
|
+
if current_player_color == PlayerColor::RED
|
81
|
+
then red
|
82
|
+
else blue
|
65
83
|
end
|
66
84
|
end
|
67
85
|
|
68
86
|
# gets the other (not the current) player
|
69
87
|
#
|
70
88
|
# @return [Player] the other (not the current) player
|
71
|
-
def
|
72
|
-
if
|
73
|
-
return
|
89
|
+
def other_player
|
90
|
+
if current_player_color == PlayerColor::RED
|
91
|
+
return blue
|
74
92
|
else
|
75
|
-
return
|
93
|
+
return red
|
76
94
|
end
|
77
95
|
end
|
78
96
|
|
79
97
|
# gets the other (not the current) player's color
|
80
98
|
#
|
81
99
|
# @return [PlayerColor] the other (not the current) player's color
|
82
|
-
def
|
83
|
-
|
84
|
-
end
|
85
|
-
|
86
|
-
# gets the start player
|
87
|
-
#
|
88
|
-
# @return [Player] the startPlayer
|
89
|
-
def startPlayer
|
90
|
-
if self.startPlayer == PlayerColor::RED
|
91
|
-
return self.red
|
92
|
-
else
|
93
|
-
return self.blue
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
# switches current player
|
98
|
-
def switchCurrentPlayer
|
99
|
-
if currentPlayer.color == PlayerColor::RED
|
100
|
-
@currentPlayer = self.blue
|
101
|
-
else
|
102
|
-
@currentPlayer = self.red
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
# prepares next turn and sets the last move
|
107
|
-
#
|
108
|
-
# @param [Move] the last move
|
109
|
-
def prepareNextTurn(lastMove)
|
110
|
-
@turn++
|
111
|
-
@lastMove = lastMove;
|
112
|
-
self.switchCurrentPlayer()
|
100
|
+
def other_player_color
|
101
|
+
PlayerColor.opponent_color(current_player_color)
|
113
102
|
end
|
114
103
|
|
115
104
|
# gets the current round
|
116
105
|
#
|
117
106
|
# @return [Integer] the current round
|
118
107
|
def round
|
119
|
-
|
120
|
-
end
|
121
|
-
|
122
|
-
# gets all possible moves
|
123
|
-
#
|
124
|
-
# @return [Array<Move>] a list of all possible moves
|
125
|
-
def getPossibleMoves
|
126
|
-
enemyFieldType = currentPlayer.color == PlayerColor::RED ? FieldType::BLUE : FieldType::RED
|
127
|
-
moves = Array.new
|
128
|
-
for x in 0..(Constants::SIZE-1)
|
129
|
-
for y in 0..(Constants::SIZE-1)
|
130
|
-
if (self.board.fields[x][y].ownerColor == PlayerColor::NONE &&
|
131
|
-
self.board.fields[x][y].type != FieldType::SWAMP &&
|
132
|
-
self.board.fields[x][y].type != enemyFieldType)
|
133
|
-
moves.push(Move.new(x, y))
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
return moves
|
108
|
+
turn / 2
|
138
109
|
end
|
139
110
|
|
140
111
|
# performs a move on the gamestate
|
141
112
|
#
|
142
113
|
# @param move [Move] the move, that will be performed
|
143
114
|
# @param player [Player] the player, who makes the move
|
144
|
-
def perform(move, player)
|
145
|
-
|
146
|
-
|
147
|
-
move.x >= 0 && move.y >= 0
|
148
|
-
if self.getPossibleMoves.include?(move)
|
149
|
-
self.board.put(move.x, move.y, player)
|
150
|
-
player.points = self.pointsForPlayer(player)
|
151
|
-
else
|
152
|
-
raise "Der Zug ist nicht möglich, denn der Platz ist bereits besetzt oder nicht besetzbar."
|
153
|
-
end
|
154
|
-
else
|
155
|
-
raise "Startkoordinaten sind nicht innerhalb des Spielfeldes."
|
156
|
-
end
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
# gets a player's points
|
161
|
-
#
|
162
|
-
# @param player [Player] the player, whos statistics will be returned
|
163
|
-
# @return [Integer] the points of the player
|
164
|
-
def playerStats(player)
|
165
|
-
return self.playerStats(player.color)
|
166
|
-
end
|
167
|
-
|
168
|
-
# gets a player's points by the player's color
|
169
|
-
#
|
170
|
-
# @param playerColor [PlayerColor] the player's color, whos statistics will be returned
|
171
|
-
# @return [Integer] the points of the player
|
172
|
-
def playerStats(playerColor)
|
173
|
-
if playerColor == PlayerColor::RED
|
174
|
-
return self.gameStats[0];
|
175
|
-
else
|
176
|
-
return self.gameStats[1]
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
# gets the players' statistics
|
181
|
-
#
|
182
|
-
# @return [Array<Integer>] the points for both players
|
183
|
-
def gameStats
|
184
|
-
stats = Array.new(2, Array.new(1))
|
185
|
-
|
186
|
-
stats[0][0] = self.red.points
|
187
|
-
stats[1][0] = self.blue.points
|
188
|
-
|
189
|
-
return stats
|
190
|
-
end
|
191
|
-
|
192
|
-
# get the players' names
|
193
|
-
#
|
194
|
-
# @return [Array<String>] the names for both players
|
195
|
-
def playerNames
|
196
|
-
return [red.displayName, blue.displayName]
|
197
|
-
end
|
198
|
-
|
199
|
-
# sets the game-ended condition
|
200
|
-
#
|
201
|
-
# @param winner [Player] the winner of the game
|
202
|
-
# @param reason [String] the winning reason
|
203
|
-
def endGame(winner, reason)
|
204
|
-
if condition.nil?
|
205
|
-
@condition = Condition.new(winner, reason)
|
115
|
+
def perform!(move, player)
|
116
|
+
move.actions.each do |action|
|
117
|
+
action.perform!(self, player)
|
206
118
|
end
|
207
119
|
end
|
208
120
|
|
209
121
|
# has the game ended?
|
210
122
|
#
|
211
123
|
# @return [Boolean] true, if the game has allready ended
|
212
|
-
def
|
213
|
-
|
124
|
+
def game_ended?
|
125
|
+
!condition.nil?
|
214
126
|
end
|
215
127
|
|
216
128
|
# gets the game's winner
|
217
129
|
#
|
218
130
|
# @return [Player] the game's winner
|
219
131
|
def winner
|
220
|
-
|
132
|
+
condition.nil? ? nil : condition.winner
|
221
133
|
end
|
222
134
|
|
223
135
|
# gets the winning reason
|
224
136
|
#
|
225
137
|
# @return [String] the winning reason
|
226
|
-
def
|
227
|
-
|
138
|
+
def winning_reason
|
139
|
+
condition.nil? ? nil : condition.reason
|
228
140
|
end
|
229
141
|
|
230
|
-
# calculates a player's points
|
142
|
+
# calculates a player's points based on the current gamestate
|
231
143
|
#
|
232
|
-
#
|
144
|
+
# @param player [Player] the player, whos point will be calculated
|
233
145
|
# @return [Integer] the points of the player
|
234
|
-
def
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
for fields in outermostFieldsInCircuit
|
267
|
-
if (playerColor == PlayerColor::RED && fields[1].y - fields[0].y > longestPath)
|
268
|
-
longestPath = fields[1].y - fields[0].y
|
269
|
-
end
|
270
|
-
if (playerColor == PlayerColor::BLUE && fields[1].x - fields[0].x > longestPath)
|
271
|
-
longestPath = fields[1].x - fields[0].x
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
|
-
return longestPath # return longestPath
|
276
|
-
end
|
277
|
-
|
278
|
-
# the following functions are helpers for the points calculation
|
279
|
-
def bottomMostFieldInCircuit(circuit)
|
280
|
-
bottomMostField = circuit[0]
|
281
|
-
for f in circuit
|
282
|
-
if f.y < bottomMostField.y
|
283
|
-
bottomMostField = f
|
284
|
-
end
|
285
|
-
end
|
286
|
-
return bottomMostField
|
287
|
-
end
|
288
|
-
|
289
|
-
def topMostFieldInCircuit(circuit)
|
290
|
-
topMostField = circuit[0]
|
291
|
-
for f in circuit
|
292
|
-
if f.y > topMostField.y
|
293
|
-
topMostField = f
|
294
|
-
end
|
295
|
-
end
|
296
|
-
return topMostField
|
297
|
-
end
|
298
|
-
|
299
|
-
def leftMostFieldInCircuit(circuit)
|
300
|
-
leftMostField = circuit[0]
|
301
|
-
for f in circuit
|
302
|
-
if f.x < leftMostField.x
|
303
|
-
leftMostField = f
|
304
|
-
end
|
305
|
-
end
|
306
|
-
return leftMostField
|
307
|
-
end
|
308
|
-
|
309
|
-
def rightMostFieldInCircuit(circuit)
|
310
|
-
rightMostField = circuit[0]
|
311
|
-
for f in circuit
|
312
|
-
if f.x > rightMostField.x
|
313
|
-
rightMostField = f
|
314
|
-
end
|
315
|
-
end
|
316
|
-
return rightMostField
|
317
|
-
end
|
318
|
-
|
319
|
-
def circuit(circuit, visited)
|
320
|
-
changed = false;
|
321
|
-
toBeAddedFields = Array.new
|
322
|
-
for f in circuit
|
323
|
-
if !visited.include?(f)
|
324
|
-
changed = true
|
325
|
-
visited.push(f)
|
326
|
-
for c in self.board.getConnections(f.x,f.y)
|
327
|
-
if !circuit.include?(self.board.fields[c.x2][c.y2])
|
328
|
-
toBeAddedFields.push(self.board.fields[c.x2][c.y2])
|
329
|
-
end
|
330
|
-
if !circuit.include?(self.board.fields[c.x1][c.y1])
|
331
|
-
toBeAddedFields.push(self.board.fields[c.x1][c.y1])
|
332
|
-
end
|
333
|
-
end
|
334
|
-
end
|
335
|
-
end
|
336
|
-
circuit.push(*toBeAddedFields)
|
337
|
-
if changed
|
338
|
-
return self.circuit(circuit, visited)
|
339
|
-
else
|
340
|
-
return circuit
|
341
|
-
end
|
342
|
-
end
|
343
|
-
|
344
|
-
end
|
146
|
+
def points_for_player(player)
|
147
|
+
player_field = board.field(player.x, player.y)
|
148
|
+
POINTS_PER_TILE * player_field.index +
|
149
|
+
POINTS_PER_PASSENGER * player.passengers +
|
150
|
+
player_field.points
|
151
|
+
end
|
152
|
+
|
153
|
+
# @return [Boolean] true if the given field is occupied by the other (not
|
154
|
+
# current) player.
|
155
|
+
def occupied_by_other_player?(field)
|
156
|
+
field.x == other_player.x && field.y == other_player.y
|
157
|
+
end
|
158
|
+
|
159
|
+
# Compared with other state.
|
160
|
+
def ==(other)
|
161
|
+
turn == other.turn &&
|
162
|
+
start_player_color == other.start_player_color &&
|
163
|
+
current_player_color == other.current_player_color &&
|
164
|
+
red == other.red &&
|
165
|
+
blue == other.blue &&
|
166
|
+
board == other.board &&
|
167
|
+
lastMove == other.lastMove &&
|
168
|
+
free_acceleration == other.free_acceleration &&
|
169
|
+
condition == other.condition
|
170
|
+
end
|
171
|
+
|
172
|
+
# Create a deep copy of the gamestate. Can be used to perform moves on without
|
173
|
+
# changing the original gamestate.
|
174
|
+
def deep_clone
|
175
|
+
Marshal.load(Marshal.dump(self))
|
176
|
+
end
|
177
|
+
end
|