Class: GameRuleLogic

Inherits:
Object
  • Object
show all
Includes:
Constants
Defined in:
lib/software_challenge_client/game_rule_logic.rb

Overview

Methoden, welche die Spielregeln von Piranhas abbilden.

Es gibt hier viele Helfermethoden, die von den beiden Hauptmethoden GameRuleLogic.valid_move? und GameRuleLogic.possible_moves benutzt werden.

Constant Summary

Constants included from Constants

Constants::GAME_IDENTIFIER, Constants::ROUND_LIMIT, Constants::SIZE

Class Method Summary collapse

Class Method Details

.add_blocked_fields(board) ⇒ Board

Fügt einem leeren Spielfeld zwei Krakenfelder hinzu. Die beiden Felder liegen nicht auf derselben Horizontalen, Vertikalen oder Diagonalen und sind mindestens zwei Felder von den Rändern des Spielbrettes entfernt.

Diese Methode ist dazu gedacht, ein initiales Spielbrett regelkonform zu generieren.

Parameters:

  • board (Board)

    Das zu modifizierende Spielbrett. Es wird nicht geprüft, ob sich auf dem Spielbrett bereits Krakenfelder befinden.

Returns:

  • (Board)

    Das modifizierte Spielbrett.


24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/software_challenge_client/game_rule_logic.rb', line 24

def self.add_blocked_fields(board)
  number_of_blocked_fields = 2
  lower_bound = 2 # first row or column, in which blocked fields are allowed
  upper_bound = 7 # last row or column, in which blocked fields are allowed

  # create a list of coordinates for fields which may be blocked
  blockable_field_coordinates = (lower_bound..upper_bound).to_a.map do |x|
    (lower_bound..upper_bound).to_a.map do |y|
      Coordinate.new(x, y)
    end
  end.flatten

  # set fields with randomly selected coordinates to blocked coordinates may
  # not lay on same horizontal, vertical or diagonal lines with other selected
  # coordinates
  number_of_blocked_fields.times do
    selected_coords = blockable_field_coordinates.sample
    board.change_field(selectedCoords, FieldType::OBSTRUCTED)
    # remove field coordinates and fields on horizontal, vertical and diagonal
    # lines:
    coordinates_to_remove = ALL_DIRECTIONS.map do |direction|
      Line.new(selected_coords, direction).to_a
    end.flatten
    blockable_field_coordinates = blockable_field_coordinates.filter do |c|
      coordinates_to_remove.none? do |to_remove|
        c.x == to_remove.x && c.y == to_remove.y
      end
    end
  end
  board
end

.count_fish(board, start, direction) ⇒ Integer

Ermittlung der Anzahl der Fische auf einer Line des Spielbrettes.

Parameters:

  • board (Board)

    Das zu betrachtende Spielbrett.

  • start (Coordinates)

    Ein Feld auf der Linie.

  • direction (LineDirection)

    Die Ausrichtung der Linie (vertikal, horizontal oder diagonal).

Returns:

  • (Integer)

    Anzahl der Fische auf der Linie.


62
63
64
65
66
67
68
# File 'lib/software_challenge_client/game_rule_logic.rb', line 62

def self.count_fish(board, start, direction)
  # filter function for fish field type
  fish = proc { |f| f.type == FieldType::RED || f.type == FieldType::BLUE }
  Line.new(start, direction).to_a.map do |p|
    board.field(p.x, p.y)
  end.select(&fish).size
end

.greatest_swarm_from_fields(board, fields_to_check, current_biggest_swarm = Set.new) ⇒ Set<Field>

Hilfsfunktion für swarm_size. Ermittelt die größte zusammenhängende Menge von Feldern aus einer gegebenen Menge von Feldern.

Parameters:

  • board (Board)

    Das zu betrachtende Spielbrett.

  • fields_to_check (Set<Field>)

    Menge der Felder, aus der die größte zusammenhängende Menge ermittelt werden soll.

  • current_biggest_swarm (Set<Field>) (defaults to: Set.new)

    Aktuell größte zusammenhängende Feldmenge. Für rekursiven Aufruf.

Returns:


198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/software_challenge_client/game_rule_logic.rb', line 198

def self.greatest_swarm_from_fields(board, fields_to_check, current_biggest_swarm = Set.new)
  # stop searching when the size of the current found biggest set is bigger
  # than the rest of the fields or if there are no more fields to check
  return current_biggest_swarm if current_biggest_swarm.size > fields_to_check.size || fields_to_check.empty?

  # start a new set of adjacent fields with the first field in fields_to_check
  current_swarm = Set.new
  field = fields_to_check.to_a.first
  fields_to_check.delete(field)
  current_swarm.add(field)

  # move all adjacent fields to the set
  loop do
    to_add = current_swarm
             .map { |f| GameRuleLogic.neighbours(board, f) }
             .flatten
             .select { |f| fields_to_check.include? f }
    break if to_add.empty?
    fields_to_check -= to_add
    current_swarm += to_add
  end

  # keep trying to find bigger sets
  if current_swarm.size > current_biggest_swarm.size
    GameRuleLogic.greatest_swarm_from_fields(
      board, fields_to_check, current_swarm
    )
  else
    GameRuleLogic.greatest_swarm_from_fields(
      board, fields_to_check, current_biggest_swarm
    )
  end
end

.inside_bounds?(coordinates) ⇒ Boolean

Prüft, ob sich die gegebenen Koordinaten innerhalb des Spielbrettes befinden.

Returns:

  • (Boolean)

87
88
89
90
91
92
# File 'lib/software_challenge_client/game_rule_logic.rb', line 87

def self.inside_bounds?(coordinates)
  coordinates.x >= 0 &&
    coordinates.x < SIZE &&
    coordinates.y >= 0 &&
    coordinates.y < SIZE
end

.move_target(move, board) ⇒ Field

Returns Das Zielfeld eines Spielzuges auf einem Spielbrett.

Returns:

  • (Field)

    Das Zielfeld eines Spielzuges auf einem Spielbrett.


80
81
82
83
# File 'lib/software_challenge_client/game_rule_logic.rb', line 80

def self.move_target(move, board)
  c = GameRuleLogic.target_coordinates(move, board)
  board.field(c.x, c.y)
end

.neighbours(board, field) ⇒ Array<Field>

Returns Alle direkten Nachbarfelder des gegebenen Feldes. Für Felder im Inneren des Spielbrettes gibt es acht Nachbarfelder. Für Randfelder vier oder drei Nachbarfelder.

Returns:

  • (Array<Field>)

    Alle direkten Nachbarfelder des gegebenen Feldes. Für Felder im Inneren des Spielbrettes gibt es acht Nachbarfelder. Für Randfelder vier oder drei Nachbarfelder.


185
186
187
188
189
190
# File 'lib/software_challenge_client/game_rule_logic.rb', line 185

def self.neighbours(board, field)
  Direction
    .map { |d| d.translate(field.coordinates) }
    .select { |c| GameRuleLogic.inside_bounds?(c) }
    .map { |c| board.field_at(c) }
end

.obstacle?(field_type, moving_player_color) ⇒ Boolean

Ermittelt, ob der gegebene Feldtyp für den Spieler mit der angegebenen Farbe ein nicht überspringbares Hindernis darstellt.

Parameters:

Returns:

  • (Boolean)

    true, falls es ein Hindernis ist, false sonst.


98
99
100
101
102
# File 'lib/software_challenge_client/game_rule_logic.rb', line 98

def self.obstacle?(field_type, moving_player_color)
  field_type == PlayerColor.field_type(
    PlayerColor.opponent_color(moving_player_color)
  )
end

.obstacle_between?(from_field, direction, to_field, color, board) ⇒ Boolean

Ermittelt, ob sich zwischen den angegebenen Feldern kein Hindernis befindet.

Parameters:

  • from_field (Coordinates)

    Startfeld

  • to_field (Coordinates)

    Zielfeld

  • direction (LineDirection)

    Ausrichtung der Linie zwischen Start- und Zielfeld.

  • color (PlayerColor)

    Farbe des ziehenden Spielers.

  • board (Board)

    Das aktuelle Spielbrett.

Returns:

  • (Boolean)

    true, falls der Spieler mit der angegebenen Farbe zwischen den beiden Punkten ein Hindernis vorfindet, false sonst.


111
112
113
114
115
116
# File 'lib/software_challenge_client/game_rule_logic.rb', line 111

def self.obstacle_between?(from_field, direction, to_field, color, board)
  Line.new(from_field, direction)
      .to_a
      .select { |c| Line.between(from_field, to_field, direction).call(c) }
      .any? { |f| GameRuleLogic.obstacle?(board.field(f.x, f.y).type, color) }
end

.possible_moves(board, field, current_player_color) ⇒ Array<Move>

Ermittelt alle möglichen Züge von einem bestimmten Feld aus.

Parameters:

  • board (Board)

    Aktuelles Spielbrett

  • field (Field)

    Das Feld, von dem die Züge ausgehen sollen.

  • current_player_color (PlayerColor)

    Farbe des Spielers, der den Zug macht.

Returns:

  • (Array<Move>)

    Liste von möglichen Zügen.


163
164
165
166
167
168
# File 'lib/software_challenge_client/game_rule_logic.rb', line 163

def self.possible_moves(board, field, current_player_color)
  Direction.map { |direction| Move.new(field.x, field.y, direction) }
           .select do |m|
             GameRuleLogic.valid_move?(m, board, current_player_color)
           end
end

.swarm_size(board, player_color) ⇒ Integer

Ermittelt die Schwarmgröße eines Spielers auf dem Spielbrett.

Parameters:

  • board (Board)

    Das zu betrachtende Spielbrett.

  • player_color (PlayerColor)

    Farbe des Spielers, für den die Schwarmgröße ermittelt werden soll.

Returns:

  • (Integer)

    Anzahl der Fische im größten Schwarm des Spielers.


174
175
176
177
178
179
180
181
182
# File 'lib/software_challenge_client/game_rule_logic.rb', line 174

def self.swarm_size(board, player_color)
  GameRuleLogic.greatest_swarm_from_fields(
    board,
    board.fields_of_type(
      PlayerColor.field_type(player_color)
    ).to_set,
    Set.new
  ).size
end

.target_coordinates(move, board) ⇒ Coordinates

Returns Die Zielkoordinaten eines Spielzuges auf einem Spielbrett.

Returns:

  • (Coordinates)

    Die Zielkoordinaten eines Spielzuges auf einem Spielbrett.


71
72
73
74
75
76
77
# File 'lib/software_challenge_client/game_rule_logic.rb', line 71

def self.target_coordinates(move, board)
  speed = GameRuleLogic.count_fish(
    board, move.from_field,
    Line.line_direction_for_direction(move.direction)
  )
  move.target_field(speed)
end

.valid_move?(move, board, current_player_color) ⇒ Boolean

Ermittelt, ob der gegebene Zug regelkonform ausgeführt werden kann.

Parameters:

  • move (Move)

    Der zu prüfende Zug

  • board (Board)

    Spielbrett, auf dem der Zug ausgeführt werden soll.

  • current_player_color (PlayerColor)

    Farbe des Spielers, der den Zug ausführen soll.

Returns:

  • (Boolean)

    true falls der Zug gültig ist, false sonst.


136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/software_challenge_client/game_rule_logic.rb', line 136

def self.valid_move?(move, board, current_player_color)
  from_field_type = board.field(move.x, move.y).type
  return false unless
    [FieldType::BLUE, FieldType::RED].include? from_field_type
  return false unless
    current_player_color == FieldType.player_color(from_field_type)

  return false unless
    GameRuleLogic.inside_bounds?(
      GameRuleLogic.target_coordinates(move, board)
    )

  target = GameRuleLogic.move_target(move, board)

  GameRuleLogic.valid_move_target(target, current_player_color, board) &&
    !GameRuleLogic.obstacle_between?(
      move.from_field,
      Line.line_direction_for_direction(move.direction),
      target, current_player_color, board
    )
end

.valid_move_target(target, moving_player_color, board) ⇒ Boolean

Ermittelt, ob der Spieler mit der angegebenen Farbe einen Fisch auf dem Feld mit den angegebenen Koordinaten besitzt.

Parameters:

  • target (Coordinates)

    Koordinaten des Feldes.

  • moving_player_color (PlayerColor)

    Farbe des Spielers, der einen Zug machen will.

  • board (Board)

    Aktuelles Spielbrett.

Returns:

  • (Boolean)

    true falls sich auf dem Feld ein Fisch mit der richtigen Farbe befindet (Rot für roten Spieler, Blau für blauen Spieler), false sonst.


123
124
125
126
127
128
129
# File 'lib/software_challenge_client/game_rule_logic.rb', line 123

def self.valid_move_target(target, moving_player_color, board)
  target_field_type = board.field(target.x, target.y).type
  target_field_type == FieldType::EMPTY ||
    target_field_type == PlayerColor.field_type(
      PlayerColor.opponent_color(moving_player_color)
    )
end