Class: GameRuleLogic
- Inherits:
-
Object
show all
- Includes:
- Constants
- Defined in:
- lib/software_challenge_client/game_rule_logic.rb
Overview
Constant Summary
Constants included
from Constants
Constants::BOARD_SIZE, Constants::GAME_IDENTIFIER, Constants::ROUND_LIMIT, Constants::SHIFT, Constants::STARTING_PIECES
Class Method Summary
collapse
-
.accessible_neighbours_except(board, start, except) ⇒ Object
-
.add_blocked_fields(board) ⇒ Board
Fügt einem leeren Spielfeld drei Brombeeren hinzu.
-
.can_move_between(board, coords1, coords2) ⇒ Object
-
.can_move_between_except(board, coords1, coords2, except) ⇒ Object
-
.empty_fields_connected_to_swarm(board) ⇒ Object
-
.get_accessible_neighbours(board, start) ⇒ Object
-
.get_accessible_neighbours_except(board, start, except) ⇒ Object
-
.get_line_between_coords(board, start, destination) ⇒ Object
-
.get_neighbour_in_direction(board, coords, direction) ⇒ Object
-
.get_neighbours(board, coordinates) ⇒ Object
-
.has_player_placed_bee(gamestate) ⇒ Object
-
.is_bee_blocked(board, color) ⇒ Object
-
.is_neighbour(start, destination) ⇒ Object
-
.is_on_board(coords) ⇒ Object
-
.is_swarm_connected(board) ⇒ Object
-
.perform_move(gamestate, move) ⇒ Object
-
.possible_drag_moves(gamestate) ⇒ Object
-
.possible_moves(gamestate) ⇒ Object
all possible moves, but will not return the skip move if no other moves are possible!.
-
.possible_set_move_destinations(board, owner) ⇒ Object
-
.possible_set_moves(gamestate) ⇒ Object
-
.shared_neighbours_of_two_coords(board, first_coords, second_coords) ⇒ Object
-
.two_fields_on_one_straight(coords1, coords2) ⇒ Object
-
.valid_move?(gamestate, move) ⇒ ?
Prueft, ob ein Spielzug fuer den gegebenen Gamestate valide ist.
-
.validate_ant_move(board, move) ⇒ Object
-
.validate_bee_move(board, move) ⇒ Object
-
.validate_beetle_move(board, move) ⇒ Object
-
.validate_destination_next_to_start(move) ⇒ Object
-
.validate_drag_move(gamestate, move) ⇒ Object
-
.validate_grasshopper_move(board, move) ⇒ Object
-
.validate_set_move(gamestate, move) ⇒ Object
-
.validate_skip_move(gamestate, move) ⇒ Object
-
.validate_spider_move(board, move) ⇒ Object
-
.winning_condition(gamestate) ⇒ Condition
Prueft, ob ein Spieler im gegebenen GameState gewonnen hat.
Class Method Details
.accessible_neighbours_except(board, start, except) ⇒ Object
263
264
265
266
267
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 263
def self.accessible_neighbours_except(board, start, except)
get_neighbours(board, start).filter do |neighbour|
neighbour.empty? && can_move_between_except(board, start, neighbour, except) && neighbour.coordinates != except
end
end
|
.add_blocked_fields(board) ⇒ Board
Fügt einem leeren Spielfeld drei Brombeeren hinzu.
Diese Methode ist dazu gedacht, ein initiales Spielbrett regelkonform zu generieren.
21
22
23
24
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 21
def self.add_blocked_fields(board)
raise "todo"
board
end
|
.can_move_between(board, coords1, coords2) ⇒ Object
222
223
224
225
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 222
def self.can_move_between(board, coords1, coords2)
shared = shared_neighbours_of_two_coords(board, coords1, coords2)
(shared.size == 1 || shared.any? { |n| n.empty? && !n.obstructed }) && shared.any? { |n| !n.pieces.empty? }
end
|
.can_move_between_except(board, coords1, coords2, except) ⇒ Object
269
270
271
272
273
274
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 269
def self.can_move_between_except(board, coords1, coords2, except)
shared = shared_neighbours_of_two_coords(board, coords1, coords2).reject do |f|
f.pieces.size == 1 && except == f.coordinates
end
(shared.size == 1 || shared.any? { |s| s.empty? && !s.obstructed }) && shared.any? { |s| !s.pieces.empty? }
end
|
.empty_fields_connected_to_swarm(board) ⇒ Object
344
345
346
347
348
349
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 344
def self.empty_fields_connected_to_swarm(board)
board.field_list
.filter { |f| f.has_owner }
.flat_map { |f| get_neighbours(board, f).filter { f.empty? } }
.uniq
end
|
.get_accessible_neighbours(board, start) ⇒ Object
286
287
288
289
290
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 286
def self.get_accessible_neighbours(board, start)
get_neighbours(board, start).filter do |neighbour|
neighbour.empty? && can_move_between(board, start, neighbour)
end
end
|
.get_accessible_neighbours_except(board, start, except) ⇒ Object
292
293
294
295
296
297
298
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 292
def self.get_accessible_neighbours_except(board, start, except)
get_neighbours(board, start).filter do |neighbour|
neighbour.empty? &&
can_move_between_except(board, start, neighbour, except) &&
neighbour.coordinates != except
end
end
|
.get_line_between_coords(board, start, destination) ⇒ Object
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 243
def self.get_line_between_coords(board, start, destination)
if (!two_fields_on_one_straight(start, destination))
raise InvalidMoveException.new("destination is not in line with start")
end
dX = start.x - destination.x
dY = start.y - destination.y
dZ = start.z - destination.z
d = (dX == 0) ? dY.abs : dX.abs
(1..(d-1)).to_a.map do |i|
board.field_at(
CubeCoordinates.new(
destination.x + i * (dX <=> 0),
destination.y + i * (dY <=> 0),
destination.z + i * (dZ <=> 0)
)
)
end
end
|
.get_neighbour_in_direction(board, coords, direction) ⇒ Object
26
27
28
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 26
def self.get_neighbour_in_direction(board, coords, direction)
board.field_at(direction.translate(coords))
end
|
.get_neighbours(board, coordinates) ⇒ Object
30
31
32
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 30
def self.get_neighbours(board, coordinates)
Direction.map { |d| get_neighbour_in_direction(board, coordinates, d) }.compact
end
|
.has_player_placed_bee(gamestate) ⇒ Object
61
62
63
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 61
def self.has_player_placed_bee(gamestate)
gamestate.deployed_pieces(gamestate.current_player_color).any? { |p| p.type == PieceType::BEE }
end
|
.is_bee_blocked(board, color) ⇒ Object
34
35
36
37
38
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 34
def self.is_bee_blocked(board, color)
bee_fields = board.field_list.select { |f| f.pieces.include?(Piece.new(color, PieceType::BEE)) }
return false if bee_fields.empty?
return get_neighbours(board, bee_fields[0].coordinates).all? { |f| !f.empty? }
end
|
.is_neighbour(start, destination) ⇒ Object
205
206
207
208
209
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 205
def self.is_neighbour(start, destination)
Direction.map do |d|
d.translate(start)
end.include?(destination)
end
|
.is_on_board(coords) ⇒ Object
56
57
58
59
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 56
def self.is_on_board(coords)
shift = (BOARD_SIZE - 1) / 2
-shift <= coords.x && coords.x <= shift && -shift <= coords.y && coords.y <= shift
end
|
.is_swarm_connected(board) ⇒ Object
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 173
def self.is_swarm_connected(board)
board_fields = board.field_list.select{ |f| !f.pieces.empty? }
return true if board_fields.empty?
visited_fields = board_fields.take 1
total_pieces = board.pieces.size
index = 0
while index < visited_fields.size
current_field = visited_fields[index]
occupied_neighbours =
get_neighbours(board, current_field.coordinates)
.filter { |f| !f.pieces.empty? }
occupied_neighbours -= visited_fields
visited_fields += occupied_neighbours
return true if visited_fields.sum{ |f| f.pieces.size } == total_pieces
index += 1
end
false
end
|
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 300
def self.perform_move(gamestate, move)
raise "Invalid move!" unless valid_move?(gamestate, move)
case move
when SetMove
gamestate.undeployed_pieces(move.piece.color).delete_at(
gamestate.undeployed_pieces(move.piece.color).index(move.piece) ||
gamestate.undeployed_pieces(move.piece.color).length
)
gamestate.board.field_at(move.destination).add_piece(move.piece)
when DragMove
piece_to_move = gamestate.board.field_at(move.start).remove_piece
gamestate.board.field_at(move.destination).add_piece(piece_to_move)
end
gamestate.turn += 1
gamestate.last_move = move
end
|
.possible_drag_moves(gamestate) ⇒ Object
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 323
def self.possible_drag_moves(gamestate)
gamestate.board.fields_of_color(gamestate.current_player_color).flat_map do |start_field|
edge_targets = empty_fields_connected_to_swarm(gamestate.board)
additional_targets =
if start_field.pieces.last.type == PieceType::BEETLE
get_neighbours(gamestate.board, start_field).uniq
else
[]
end
edge_targets + additional_targets.map do |destination|
move = DragMove.new(start_field, destination)
begin
valid_move?(gamestate, move)
move
rescue InvalidMoveException
nil
end
end.compact
end
end
|
.possible_moves(gamestate) ⇒ Object
all possible moves, but will not return the skip move if no other moves are possible!
319
320
321
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 319
def self.possible_moves(gamestate)
possible_set_moves(gamestate) + possible_drag_moves(gamestate)
end
|
.possible_set_move_destinations(board, owner) ⇒ Object
351
352
353
354
355
356
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 351
def self.possible_set_move_destinations(board, owner)
board.fields_of_color(owner)
.flat_map { |f| get_neighbours(board, f).filter { |f| f.empty? } }
.uniq
.filter { |f| get_neighbours(board, f).all? { |n| n.color != owner.opponent } }
end
|
.possible_set_moves(gamestate) ⇒ Object
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 358
def self.possible_set_moves(gamestate)
undeployed = gamestate.undeployed_pieces(gamestate.current_player_color)
set_destinations =
if (undeployed.size == STARTING_PIECES.size)
if (gamestate.undeployed_pieces(gamestate.other_player_color).size == STARTING_PIECES.size)
gamestate.board.field_list.filter { |f| f.empty? }
else
gamestate.board
.fields_of_color(gamestate.other_player_color)
.flat_map do |f|
GameRuleLogic.get_neighbours(gamestate.board, f).filter(&:empty?)
end
end
else
possible_set_move_destinations(gamestate.board, gamestate.current_player_color)
end
possible_piece_types =
if (!has_player_placed_bee(gamestate) && gamestate.turn > 5)
[PieceType::BEE]
else
undeployed.map(&:type).uniq
end
set_destinations
.flat_map do |d|
possible_piece_types.map do |u|
SetMove.new(Piece.new(gamestate.current_player_color, u), d)
end
end
end
|
.shared_neighbours_of_two_coords(board, first_coords, second_coords) ⇒ Object
211
212
213
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 211
def self.shared_neighbours_of_two_coords(board, first_coords, second_coords)
get_neighbours(board, first_coords) & get_neighbours(board, second_coords)
end
|
.two_fields_on_one_straight(coords1, coords2) ⇒ Object
239
240
241
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 239
def self.two_fields_on_one_straight(coords1, coords2)
return coords1.x == coords2.x || coords1.y == coords2.y || coords1.z == coords2.z
end
|
.valid_move?(gamestate, move) ⇒ ?
Prueft, ob ein Spielzug fuer den gegebenen Gamestate valide ist
45
46
47
48
49
50
51
52
53
54
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 45
def self.valid_move?(gamestate, move)
case move
when SetMove
validate_set_move(gamestate, move)
when DragMove
validate_drag_move(gamestate, move)
when SkipMove
validate_skip_move(gamestate, move)
end
end
|
.validate_ant_move(board, move) ⇒ Object
160
161
162
163
164
165
166
167
168
169
170
171
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 160
def self.validate_ant_move(board, move)
visited_fields = [move.start]
index = 0
while index < visited_fields.size
current_field = visited_fields[index]
new_fields = accessible_neighbours_except(board, current_field, move.start).reject { |f| visited_fields.include? f }
return true if new_fields.map(&:coordinates).include?(move.destination)
visited_fields += new_fields
index += 1
end
raise InvalidMoveException.new("No path found for ant move", move)
end
|
.validate_bee_move(board, move) ⇒ Object
215
216
217
218
219
220
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 215
def self.validate_bee_move(board, move)
validate_destination_next_to_start(move)
if (!can_move_between(board, move.start, move.destination))
raise InvalidMoveException.new("There is no path to your destination", move)
end
end
|
.validate_beetle_move(board, move) ⇒ Object
192
193
194
195
196
197
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 192
def self.validate_beetle_move(board, move)
validate_destination_next_to_start(move)
if ((shared_neighbours_of_two_coords(board, move.start, move.destination) + [board.field_at(move.destination), board.field_at(move.start)]).all? { |f| f.pieces.empty? })
raise InvalidMoveException.new("Beetle has to move along swarm", move)
end
end
|
.validate_destination_next_to_start(move) ⇒ Object
199
200
201
202
203
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 199
def self.validate_destination_next_to_start(move)
if (!is_neighbour(move.start, move.destination))
raise InvalidMoveException.new("Destination field is not next to start field", move)
end
end
|
.validate_drag_move(gamestate, move) ⇒ Object
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
152
153
154
155
156
157
158
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 111
def self.validate_drag_move(gamestate, move)
unless has_player_placed_bee(gamestate)
raise InvalidMoveException.new("You have to place the bee to be able to perform dragmoves", move)
end
if (!is_on_board(move.destination) || !is_on_board(move.start))
raise InvalidMoveException.new("The Move is out of bounds", move)
end
if (gamestate.board.field_at(move.start).pieces.empty?)
raise InvalidMoveException.new("There is no piece to move", move)
end
piece_to_drag = gamestate.board.field_at(move.start).pieces.last
if (piece_to_drag.owner != gamestate.current_player_color)
raise InvalidMoveException.new("Trying to move piece of the other player", move)
end
if (move.start == move.destination)
raise InvalidMoveException.new("Destination and start are equal", move)
end
if (!gamestate.board.field_at(move.destination).pieces.empty? && piece_to_drag.type != PieceType::BEETLE)
raise InvalidMoveException.new("Only beetles are allowed to climb on other Pieces", move)
end
board_without_piece = gamestate.board.clone
board_without_piece.field_at(move.start).pieces.pop
if (!is_swarm_connected(board_without_piece))
raise InvalidMoveException.new("Moving piece would disconnect swarm", move)
end
case piece_to_drag.type
when PieceType::ANT
validate_ant_move(board_without_piece, move)
when PieceType::BEE
validate_bee_move(board_without_piece, move)
when PieceType::BEETLE
validate_beetle_move(board_without_piece, move)
when PieceType::GRASSHOPPER
validate_grasshopper_move(board_without_piece, move)
when PieceType::SPIDER
validate_spider_move(board_without_piece, move)
end
true
end
|
.validate_grasshopper_move(board, move) ⇒ Object
227
228
229
230
231
232
233
234
235
236
237
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 227
def self.validate_grasshopper_move(board, move)
if (!two_fields_on_one_straight(move.start, move.destination))
raise InvalidMoveException.new("Grasshopper can only move straight lines", move)
end
if (is_neighbour(move.start, move.destination))
raise InvalidMoveException.new("Grasshopper has to jump over at least one piece", move)
end
if (get_line_between_coords(board, move.start, move.destination).any? { |f| f.empty? })
raise InvalidMoveException.new("Grasshopper can only jump over occupied fields, not empty ones", move)
end
end
|
.validate_set_move(gamestate, move) ⇒ Object
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
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 65
def self.validate_set_move(gamestate, move)
unless is_on_board(move.destination)
raise InvalidMoveException.new("Piece has to be placed on board. Destination ${move.destination} is out of bounds.", move)
end
unless gamestate.board.field_at(move.destination).empty?
raise InvalidMoveException.new("Set destination is not empty!", move)
end
owned_fields = gamestate.board.fields_of_color(gamestate.current_player_color)
if owned_fields.empty?
other_player_fields = gamestate.board.fields_of_color(gamestate.other_player_color)
if !other_player_fields.empty?
unless other_player_fields.map{ |of| get_neighbours(gamestate.board, of.coordinates).map{ |n| n.coordinates } }.flatten.include?(move.destination)
raise InvalidMoveException.new("Piece has to be placed next to other players piece", move)
end
end
else
if gamestate.round == 3 && !has_player_placed_bee(gamestate) && move.piece.type != PieceType::BEE
raise InvalidMoveException.new("The bee must be placed in fourth round latest", move)
end
if !gamestate.undeployed_pieces(gamestate.current_player_color).include?(move.piece)
raise InvalidMoveException.new("Piece is not a undeployed piece of the current player", move)
end
destination_neighbours = get_neighbours(gamestate.board, move.destination)
if !destination_neighbours.any? { |f| f.color == gamestate.current_player_color }
raise InvalidMoveException.new("A newly placed piece must touch an own piece", move)
end
if destination_neighbours.any? { |f| f.color == gamestate.other_player_color }
raise InvalidMoveException.new("A newly placed is not allowed to touch an opponent's piece", move)
end
end
true
end
|
.validate_skip_move(gamestate, move) ⇒ Object
101
102
103
104
105
106
107
108
109
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 101
def self.validate_skip_move(gamestate, move)
if !possible_moves(gamestate).empty?
raise InvalidMoveException.new("Skipping a turn is only allowed when no other moves can be made.", move)
end
if gamestate.round == 3 && !has_player_placed_bee(gamestate)
raise InvalidMoveException.new("The bee must be placed in fourth round latest", move)
end
true
end
|
.validate_spider_move(board, move) ⇒ Object
276
277
278
279
280
281
282
283
284
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 276
def self.validate_spider_move(board, move)
found = get_accessible_neighbours(board, move.start).any? do |depth_one|
get_accessible_neighbours_except(board, depth_one, move.start).any? do |depth_two|
get_accessible_neighbours_except(board, depth_two, move.start).reject{ |f| f.coordinates == depth_one.coordinates }.any? { |f| move.destination == f.coordinates }
end
end
return true if (found)
raise InvalidMoveException.new("No path found for spider move", move)
end
|
.winning_condition(gamestate) ⇒ Condition
Prueft, ob ein Spieler im gegebenen GameState gewonnen hat.
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
|
# File 'lib/software_challenge_client/game_rule_logic.rb', line 395
def self.winning_condition(gamestate)
raise "Not implemented yet!"
winner_by_single_swarm = [PlayerColor::RED, PlayerColor::BLUE].select do |player_color|
GameRuleLogic.swarm_size(gamestate.board, player_color) ==
gamestate.board.fields_of_type(PlayerColor.field_type(player_color)).size
end
if winner_by_single_swarm.any? && gamestate.turn.even?
return Condition.new(nil, "Unentschieden.") if winner_by_single_swarm.size == 2
return Condition.new(winner_by_single_swarm.first, "Schwarm wurde vereint.")
end
player_with_biggest_swarm = [PlayerColor::RED, PlayerColor::BLUE].sort_by do |player_color|
GameRuleLogic.swarm_size(gamestate.board, player_color)
end.reverse.first
return Condition.new(player_with_biggest_swarm, "Rundenlimit erreicht, Schwarm mit den meisten Fischen gewinnt.") if gamestate.turn == 60
nil
end
|