software_challenge_client 21.0.1 → 22.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fb6f7046235b14fdf74c293c8c4f1b40a014373f949e9c9987f1ee36db6e579f
4
- data.tar.gz: 95cb06324e9ae3ef77e7a76200bd45f47af3176f18f744f138c835d14ef7ade2
3
+ metadata.gz: 5409d523f45de264ef1616783fc0e901ea63460f831df5b65a014cc5effba23c
4
+ data.tar.gz: 1e9d622dbb0008fff464ca1e39a02ae3bab89192f07674ebd0f63b09b45f0fab
5
5
  SHA512:
6
- metadata.gz: 382fe8164ebb55803018b0ebec3c04061d9fbc65e770702383190ea9a5279f5cb6e0ddcbb12e3a3b3057d31450f416f4388d3ddafb1e14447c15bd2fb16a937a
7
- data.tar.gz: fa76ba63c8634597486a9b34665a0c731870a25ace4ddef3d4b9b05206b6c31e97077a2bf12106193e321dfec6225c99d3361435d8938229169e77ecad9b0c3a
6
+ metadata.gz: 02e77e4cc105dbccc0c52df6c3d3f7486cf36015d49ed08d6132c69983724e1cdff0fef12c0267b348137c5693b209aff80123dd5a297d28e0e6227ead3a9bbc
7
+ data.tar.gz: 9ee8e0a060c2b214353ca95647dd40bc844043688354663a9ccdd959269f38a70c85fae6bc531ec0eec5a862522e01c876984bc4bf1b4920de8e67f80d311c93
data/Dockerfile CHANGED
@@ -1,3 +1,3 @@
1
- FROM ruby:2.6.5
1
+ FROM ruby:2.6.6
2
2
 
3
3
  RUN gem install --no-document software_challenge_client
data/RELEASES.md CHANGED
@@ -1,3 +1,15 @@
1
+ = 21.2.0
2
+
3
+ Adjustments for Backend version 21.2.0
4
+
5
+ = 21.1.0
6
+
7
+ Pieces are now mutable and `==` of pieces considers rotated shapes which result in the same covered board fields as equal.
8
+
9
+ = 21.0.2
10
+
11
+ Fixed problem which caused `last_move` of a `GameState` always be `nil` (thanks to wollw!).
12
+
1
13
  = 21.0.1
2
14
 
3
15
  Improved performance and defined Ruby version 2.5.5 as minimum requirement
data/bin/console CHANGED
File without changes
data/bin/setup CHANGED
File without changes
data/develop.sh CHANGED
File without changes
data/example/main.rb CHANGED
File without changes
data/example/start.bat CHANGED
File without changes
data/generate-authors.sh CHANGED
File without changes
@@ -4,7 +4,6 @@ module SoftwareChallengeClient
4
4
  require 'software_challenge_client/client_interface.rb'
5
5
  require 'software_challenge_client/color.rb'
6
6
  require 'software_challenge_client/condition.rb'
7
- require 'software_challenge_client/coordinate_set.rb'
8
7
  require 'software_challenge_client/coordinates.rb'
9
8
  require 'software_challenge_client/debug_hint.rb'
10
9
  require 'software_challenge_client/field.rb'
@@ -13,16 +12,14 @@ module SoftwareChallengeClient
13
12
  require 'software_challenge_client/has_hints.rb'
14
13
  require 'software_challenge_client/invalid_move_exception.rb'
15
14
  require 'software_challenge_client/logging.rb'
15
+ require 'software_challenge_client/move.rb'
16
16
  require 'software_challenge_client/network.rb'
17
17
  require 'software_challenge_client/piece.rb'
18
- require 'software_challenge_client/piece_shape.rb'
18
+ require 'software_challenge_client/piece_type.rb'
19
19
  require 'software_challenge_client/player.rb'
20
- require 'software_challenge_client/player_type.rb'
21
20
  require 'software_challenge_client/protocol.rb'
22
- require 'software_challenge_client/rotation.rb'
23
21
  require 'software_challenge_client/runner.rb'
24
- require 'software_challenge_client/set_move.rb'
25
- require 'software_challenge_client/skip_move.rb'
22
+ require 'software_challenge_client/team.rb'
26
23
  require 'software_challenge_client/util/constants.rb'
27
24
  require 'software_challenge_client/version.rb'
28
25
  end
@@ -5,31 +5,16 @@ require_relative './util/constants'
5
5
  require_relative 'game_state'
6
6
  require_relative 'field'
7
7
 
8
- # Ein Spielbrett fuer Blokus
8
+ # Ein Spielbrett fuer Ostseeschach
9
9
  class Board
10
10
  include Constants
11
+
11
12
  # @!attribute [r] fields
12
13
  # @note Besser über die {#field} Methode auf Felder zugreifen.
13
14
  # @return [Array<Array<Field>>] Ein Feld wird an der Position entsprechend
14
15
  # seiner x und y Coordinates im Array gespeichert.
15
16
  attr_reader :fields
16
17
 
17
- # @!attribute [r] deployed_blue_pieces
18
- # @return [Array<PieceShape>] Die blauen, gesetzten Spielsteine
19
- attr_accessor :deployed_blue_pieces
20
-
21
- # @!attribute [r] deployed_yellow_pieces
22
- # @return [Array<PieceShape>] Die gelben, gesetzten Spielsteine
23
- attr_accessor :deployed_yellow_pieces
24
-
25
- # @!attribute [r] deployed_red_pieces
26
- # @return [Array<PieceShape>] Die roten, gesetzten Spielsteine
27
- attr_accessor :deployed_red_pieces
28
-
29
- # @!attribute [r] deployed_green_pieces
30
- # @return [Array<PieceShape>] Die grünen, gesetzten Spielsteine
31
- attr_accessor :deployed_green_pieces
32
-
33
18
  # Erstellt ein neues leeres Spielbrett.
34
19
  def initialize(fields = [])
35
20
  @fields = Board.empty_game_field
@@ -91,39 +76,19 @@ class Board
91
76
  field(coordinates.x, coordinates.y)
92
77
  end
93
78
 
94
- # TODO: Redo this recursively, starting from the corresponding corner and then moving alongside edges and corners
79
+ def fields_of_color(color)
80
+ fields = []
95
81
 
96
- # Alle Felder einer bestimmten Farbe
97
- #
98
- # @param color [Color] Die Farbe der Felder
99
- # @return [Array<Field>] Eine Liste aller felder, die die gegebene Farbe haben
100
- def fields_of_color(color, fields = [Coordinates.new(0, 0),
101
- Coordinates.new(0, BOARD_SIZE - 1),
102
- Coordinates.new(BOARD_SIZE - 1, BOARD_SIZE - 1),
103
- Coordinates.new(BOARD_SIZE - 1, 0)].select { |it| field_at(it).color == color })
104
- copy = Array.new(fields)
105
-
106
- copy.each do |field|
107
- [Coordinates.new(1, 0),
108
- Coordinates.new(1, -1),
109
- Coordinates.new(0, -1),
110
- Coordinates.new(-1, -1),
111
- Coordinates.new(-1, 0),
112
- Coordinates.new(-1, 1),
113
- Coordinates.new(0, 1),
114
- Coordinates.new(1, 1)].each do |neighbor|
115
- new_field = field + neighbor
116
- next unless Board.contains(new_field) && @fields[new_field.x][new_field.y].color == color
117
-
118
- fields << new_field unless fields.include?(new_field)
82
+ (0...BOARD_SIZE).to_a.map do |x|
83
+ (0...BOARD_SIZE).to_a.map do |y|
84
+ f = field(x,y)
85
+ if (f.color == color)
86
+ fields << f
87
+ end
119
88
  end
120
89
  end
121
90
 
122
- if copy.count == fields.count
123
- fields
124
- else
125
- fields_of_color(color, fields)
126
- end
91
+ fields
127
92
  end
128
93
 
129
94
  # @param it [Coordinates] Die zu untersuchenden Koordinaten
@@ -132,21 +97,6 @@ class Board
132
97
  it.x >= 0 && it.y >= 0 && it.x < BOARD_SIZE && it.y < BOARD_SIZE
133
98
  end
134
99
 
135
- # @param color [Color] Die Farbe der Steine
136
- # @return [Array<PieceShape>] Eine Liste aller Steintypen, die die gegebene Farbe noch nicht gespielt hat
137
- def deployed_pieces(color)
138
- case color
139
- when Color::RED
140
- deployed_red_pieces
141
- when Color::BLUE
142
- deployed_blue_pieces
143
- when Color::YELLOW
144
- deployed_yellow_pieces
145
- when Color::GREEN
146
- deployed_green_pieces
147
- end
148
- end
149
-
150
100
  # @return eine unabhaengige Kopie des Spielbretts
151
101
  def clone
152
102
  Marshal.load(Marshal.dump(self))
@@ -2,15 +2,25 @@
2
2
 
3
3
  require 'typesafe_enum'
4
4
 
5
- # Die Spielsteinfarben. BLUE, YELLOW, RED und GREEN
5
+ require_relative 'team'
6
+
7
+ # TODO 2022: Replace with bool?
8
+ # Die Spielsteinfarben. BLUE, und RED
6
9
  class Color < TypesafeEnum::Base
7
10
  new :BLUE, 'B'
8
- new :YELLOW, 'Y'
9
11
  new :RED, 'R'
10
- new :GREEN, 'G'
11
12
 
12
13
  # Gibt den color namen zurück
13
14
  def to_s
14
15
  self.key.to_s
15
16
  end
17
+
18
+ # Gibt das zugehörige Team zurück
19
+ def to_t
20
+ if self.key == :RED
21
+ Team::ONE
22
+ else
23
+ Team::TWO
24
+ end
25
+ end
16
26
  end
@@ -4,47 +4,66 @@
4
4
  # Ein Feld des Spielfelds. Ein Spielfeld ist durch die Koordinaten eindeutig
5
5
  # identifiziert.
6
6
  class Field
7
- # @!attribute [rw] color
8
- # @return [Color] Farbe des überdeckenden Spielsteins, falls vorhanden, sonst
9
- # nil
10
- attr_accessor :color
11
7
  # @!attribute [r] coordinates
12
8
  # @return [Coordinates] die X-Y-Koordinaten des Feldes
13
9
  attr_reader :coordinates
14
10
 
11
+ # @!attribute [rw] piece
12
+ # @return [Piece] das Piece auf diesem Feld, falls vorhanden, sonst nil
13
+ attr_accessor :piece
14
+
15
15
  # Erstellt ein neues leeres Feld.
16
16
  #
17
17
  # @param x [Integer] X-Koordinate
18
18
  # @param y [Integer] Y-Koordinate
19
19
  # @param color [Color] Farbe des Spielsteins, der das Feld überdeckt, nil falls kein Spielstein es überdeckt
20
- def initialize(x, y, color = nil)
21
- @color = color
20
+ def initialize(x, y, piece = nil)
21
+ @piece = piece
22
22
  @coordinates = Coordinates.new(x, y)
23
23
  end
24
24
 
25
25
  # Vergleicht zwei Felder. Felder sind gleich, wenn sie gleiche Koordinaten und
26
- # gleichen Typ haben.
26
+ # den gleichen Spielstein haben.
27
27
  # @return [Boolean] true bei Gleichheit, sonst false.
28
28
  def ==(other)
29
- coordinates == other.coordinates &&
30
- color == other.color
29
+ !other.nil? && coordinates == other.coordinates && piece == other.piece
31
30
  end
32
31
 
32
+ # @return [Integer] X-Koordinate des Felds
33
33
  def x
34
34
  coordinates.x
35
35
  end
36
36
 
37
+ # @return [Integer] Y-Koordinate des Felds
37
38
  def y
38
39
  coordinates.y
39
40
  end
40
41
 
42
+ # @return [Team] Team des Pieces auf dem Feld
43
+ def team
44
+ if piece.nil?
45
+ nil
46
+ else
47
+ piece.color.to_t
48
+ end
49
+ end
50
+
51
+ # @return [PieceColor] Farbe des Pieces auf dem Feld
52
+ def color
53
+ if piece.nil?
54
+ nil
55
+ else
56
+ piece.color
57
+ end
58
+ end
59
+
41
60
  # @return [Boolean] true, wenn das Feld nicht durch einen Spielstein überdeckt ist, sonst false
42
61
  def empty?
43
- color.nil?
62
+ piece.nil?
44
63
  end
45
64
 
46
65
  # @return [String] Textuelle Darstellung des Feldes.
47
66
  def to_s
48
- empty? ? '_' : color.value
67
+ empty? ? '__' : piece.to_ss
49
68
  end
50
69
  end
@@ -2,11 +2,11 @@
2
2
 
3
3
  require_relative './util/constants'
4
4
  require_relative 'invalid_move_exception'
5
- require_relative 'set_move'
5
+ require_relative 'move'
6
6
 
7
7
  require 'set'
8
8
 
9
- # Methoden, welche die Spielregeln von Blokus abbilden.
9
+ # Methoden, welche die Spielregeln von Ostseeschach abbilden.
10
10
  #
11
11
  # Es gibt hier viele Helfermethoden, die von den beiden Hauptmethoden {GameRuleLogic#valid_move?}
12
12
  # und {GameRuleLogic.possible_moves} benutzt werden.
@@ -19,251 +19,69 @@ class GameRuleLogic
19
19
 
20
20
  # Gibt alle möglichen Züge für den Spieler zurück, der in der gamestate dran ist.
21
21
  # Diese ist die wichtigste Methode dieser Klasse für Schüler.
22
- #
23
22
  # @param gamestate [GameState] Der zu untersuchende Spielstand.
23
+ #
24
+ # @return [Array<Move>] Die möglichen Moves
24
25
  def self.possible_moves(gamestate)
25
- re = possible_setmoves(gamestate)
26
+ moves = []
27
+ fields = gamestate.board.fields_of_color(gamestate.current_player.color)
26
28
 
27
- re << SkipMove.new unless gamestate.is_first_move?
29
+ fields.each do |f|
30
+ moves.push(*moves_for_piece(gamestate, f.piece))
31
+ end
28
32
 
29
- re
33
+ moves.select { |m| valid_move?(gamestate, m) }.to_a
30
34
  end
31
35
 
32
36
  # Gibt einen zufälligen möglichen Zug zurück
33
37
  # @param gamestate [GameState] Der zu untersuchende Spielstand.
38
+ #
39
+ # @return [Move] Ein möglicher Move
34
40
  def self.possible_move(gamestate)
35
41
  possible_moves(gamestate).sample
36
42
  end
37
43
 
38
- # Gibt alle möglichen Legezüge zurück
39
- # @param gamestate [GameState] Der zu untersuchende Spielstand.
40
- def self.possible_setmoves(gamestate)
41
- if gamestate.is_first_move?
42
- possible_start_moves(gamestate)
43
- else
44
- all_possible_setmoves(gamestate).flatten
45
- end
46
- end
47
-
48
- # Gibt alle möglichen Legezüge in der ersten Runde zurück
49
- # @param gamestate [GameState] Der zu untersuchende Spielstand.
50
- def self.possible_start_moves(gamestate)
51
- color = gamestate.current_color
52
- shape = gamestate.start_piece
53
- area1 = shape.dimension
54
- area2 = Coordinates.new(area1.y, area1.x)
55
- moves = Set[]
56
-
57
- # Hard code corners for most efficiency (and because a proper algorithm would be pretty illegible here)
58
- # Upper Left
59
- moves.merge(moves_for_shape_on(color, shape, Coordinates.new(0, 0)))
60
-
61
- # Upper Right
62
- moves.merge(moves_for_shape_on(color, shape, Coordinates.new(BOARD_SIZE - area1.x, 0)))
63
- moves.merge(moves_for_shape_on(color, shape, Coordinates.new(BOARD_SIZE - area2.x, 0)))
64
-
65
- # Lower Left
66
- moves.merge(moves_for_shape_on(color, shape, Coordinates.new(0, BOARD_SIZE - area1.y)))
67
- moves.merge(moves_for_shape_on(color, shape, Coordinates.new(0, BOARD_SIZE - area2.y)))
68
-
69
- # Lower Right
70
- moves.merge(moves_for_shape_on(color, shape, Coordinates.new(BOARD_SIZE - area1.x, BOARD_SIZE - area1.y)))
71
- moves.merge(moves_for_shape_on(color, shape, Coordinates.new(BOARD_SIZE - area2.x, BOARD_SIZE - area2.y)))
72
-
73
- moves.select { |m| valid_set_move?(gamestate, m) }.to_a
74
- end
75
-
76
- # Hilfsmethode um Legezüge für eine [PieceShape] zu berechnen.
77
- # @param color [Color] Die Farbe der Spielsteine der Züge
78
- # @param shape [PieceShape] Die Form der Spielsteine der Züge
79
- # @param position [Coordinates] Die Position der Spielsteine der Züge
80
- def self.moves_for_shape_on(color, shape, position)
81
- moves = Set[]
82
- Rotation.each do |r|
83
- [true, false].each do |f|
84
- moves << SetMove.new(Piece.new(color, shape, r, f, position))
85
- end
86
- end
87
- moves
88
- end
89
-
90
- # Gib eine Liste aller möglichen Legezüge zurück, auch wenn es die erste Runde ist.
91
- # @param gamestate [GameState] Der zu untersuchende Spielstand.
92
- def self.all_possible_setmoves(gamestate)
93
- moves = []
94
- fields = valid_fields(gamestate)
95
- gamestate.undeployed_pieces(gamestate.current_color).each do |p|
96
- (moves << possible_moves_for_shape(gamestate, p, fields)).flatten
97
- end
98
- moves
99
- end
100
-
101
- # Gibt eine Liste aller möglichen SetMoves für diese Form zurück.
44
+ # Hilfsmethode um Legezüge für einen [Piece] zu berechnen.
102
45
  # @param gamestate [GameState] Der zu untersuchende Spielstand.
103
- # @param shape Die [PieceShape], die die Züge nutzen sollen
46
+ # @param piece [Piece] Der Typ des Spielsteines
104
47
  #
105
- # @return Alle möglichen Züge mit der Form
106
- def self.possible_moves_for_shape(gamestate, shape, fields = valid_fields(gamestate))
107
- color = gamestate.current_color
108
-
48
+ # @return [Array<Move>] Die möglichen Moves
49
+ def self.moves_for_piece(gamestate, piece)
109
50
  moves = Set[]
110
- fields.each do |field|
111
- shape.unique_transforms().each do |t|
112
- piece = Piece.new(color, shape, t.r, t.f, Coordinates.new(0, 0))
113
- piece.coords.each do |pos|
114
- moves << SetMove.new(Piece.new(color, shape, t.r, t.f, Coordinates.new(field.x - pos.x, field.y - pos.y)))
115
- end
116
- end
117
- end
118
- moves.select { |m| valid_set_move?(gamestate, m) }.to_a
119
- end
120
-
121
- # Gibt eine Liste aller Felder zurück, an denen möglicherweise Züge gemacht werden kann.
122
- # @param gamestate [GameState] Der zu untersuchende Spielstand.
123
- def self.valid_fields(gamestate)
124
- color = gamestate.current_color
125
- board = gamestate.board
126
- fields = Set[]
127
- board.fields_of_color(color).each do |field|
128
- [Coordinates.new(field.x - 1, field.y - 1),
129
- Coordinates.new(field.x - 1, field.y + 1),
130
- Coordinates.new(field.x + 1, field.y - 1),
131
- Coordinates.new(field.x + 1, field.y + 1)].each do |corner|
132
- next unless Board.contains(corner)
133
- next unless board[corner].empty?
134
- next if neighbor_of_color?(board, Field.new(corner.x, corner.y), color)
135
-
136
- fields << corner
137
- end
138
- end
139
- fields
140
- end
141
-
142
- # Überprüft, ob das gegebene Feld ein Nachbarfeld mit der Farbe [color] hat
143
- # @param board [Board] Das aktuelle Board
144
- # @param field [Field] Das zu überprüfende Feld
145
- # @param color [Color] Nach der zu suchenden Farbe
146
- def self.neighbor_of_color?(board, field, color)
147
- [Coordinates.new(field.x - 1, field.y),
148
- Coordinates.new(field.x, field.y - 1),
149
- Coordinates.new(field.x + 1, field.y),
150
- Coordinates.new(field.x, field.y + 1)].any? do |neighbor|
151
- Board.contains(neighbor) && board[neighbor].color == color
51
+ piece.target_coords.each do |c|
52
+ moves << Move.new(piece.position, c)
152
53
  end
54
+ moves.select { |m| valid_move?(gamestate, m) }.to_a
153
55
  end
154
56
 
155
- # # Return a list of all moves, impossible or not.
156
- # # There's no real usage, except maybe for cases where no Move validation happens
157
- # # if `Constants.VALIDATE_MOVE` is false, then this function should return the same
158
- # # Set as `::getPossibleMoves`
159
- # def self.get_all_set_moves()
160
- # moves = []
161
- # Color.each do |c|
162
- # PieceShape.each do |s|
163
- # Rotation.each do |r|
164
- # [false, true].each do |f|
165
- # (0..BOARD_SIZE-1).to_a.each do |x|
166
- # (0..BOARD_SIZE-1).to_a.each do |y|
167
- # moves << SetMove.new(Piece.new(c, s, r, f, Coordinates.new(x, y)))
168
- # end
169
- # end
170
- # end
171
- # end
172
- # end
173
- # end
174
- # moves
175
- # end
176
-
177
57
  # --- Move Validation ------------------------------------------------------------
178
58
 
179
59
  # Prüft, ob der gegebene [Move] zulässig ist.
180
60
  # @param gamestate [GameState] Der zu untersuchende Spielstand.
181
- # @param move der zu überprüfende Zug
61
+ # @param move [Move] Der zu überprüfende Zug
182
62
  #
183
63
  # @return ob der Zug zulässig ist
184
64
  def self.valid_move?(gamestate, move)
185
- if move.instance_of? SkipMove
186
- !gamestate.is_first_move?
187
- else
188
- valid_set_move?(gamestate, move)
189
- end
190
- end
65
+ return false unless gamestate.current_player.color == move.piece(gamestate).color
191
66
 
192
- # Prüft, ob der gegebene [SetMove] zulässig ist.
193
- # @param gamestate [GameState] der aktuelle Spielstand
194
- # @param move [SetMove] der zu überprüfende Zug
195
- #
196
- # @return ob der Zug zulässig ist
197
- def self.valid_set_move?(gamestate, move)
198
- # Check whether the color's move is currently active
199
- return false if move.piece.color != gamestate.current_color
67
+ return false unless gamestate.board.in_bounds?(move.to)
200
68
 
201
- # Check whether the shape is valid
202
- if gamestate.is_first_move?
203
- return false if move.piece.kind != gamestate.start_piece
204
- elsif !gamestate.undeployed_pieces(move.piece.color).include?(move.piece.kind)
205
- return false
206
- end
69
+ return false if gamestate.board.field_at(move.to).color == move.piece(gamestate).color
207
70
 
208
- # Check whether the piece can be placed
209
- move.piece.coords.each do |it|
210
- return false unless gamestate.board.in_bounds?(it)
211
- return false if obstructed?(gamestate.board, it)
212
- return false if borders_on_color?(gamestate.board, it, move.piece.color)
213
- end
71
+ return false unless move.piece(gamestate).target_coords.include? move.to
214
72
 
215
- if gamestate.is_first_move?
216
- # Check if it is placed correctly in a corner
217
- return false if move.piece.coords.none? { |it| corner?(it) }
218
- else
219
- # Check if the piece is connected to at least one tile of same color by corner
220
- return false if move.piece.coords.none? { |it| corners_on_color?(gamestate.board, it, move.piece.color) }
221
- end
73
+ # TODO 2022: Forgot checks?
222
74
 
223
75
  true
224
76
  end
225
77
 
226
- # Überprüft, ob das gegebene Feld ein Nachbarfeld mit der Farbe [color] hat
227
- # @param board [Board] Das aktuelle Spielbrett
228
- # @param field [Field] Das zu überprüfende Feld
229
- # @param color [Color] Nach der zu suchenden Farbe
230
- def self.borders_on_color?(board, position, color)
231
- [Coordinates.new(1, 0), Coordinates.new(0, 1), Coordinates.new(-1, 0), Coordinates.new(0, -1)].any? do |it|
232
- if board.in_bounds?(position + it)
233
- board[position + it].color == color
234
- else
235
- false
236
- end
237
- end
238
- end
239
-
240
- # Überprüft, ob das gegebene Feld ein diagonales Nachbarfeld mit der Farbe [color] hat
241
- # @param board [Board] Das aktuelle Spielbrett
242
- # @param position [Field] Das zu überprüfende Feld
243
- # @param color [Color] Nach der zu suchenden Farbe
244
- def self.corners_on_color?(board, position, color)
245
- [Coordinates.new(1, 1), Coordinates.new(1, -1), Coordinates.new(-1, -1), Coordinates.new(-1, 1)].any? do |it|
246
- board.in_bounds?(position + it) && board[position + it].color == color
247
- end
248
- end
249
-
250
- # Überprüft, ob die gegebene [position] an einer Ecke des Boards liegt.
251
- # @param position [Coordinates] Die zu überprüfenden Koordinaten
252
- def self.corner?(position)
253
- corner = [
254
- Coordinates.new(0, 0),
255
- Coordinates.new(BOARD_SIZE - 1, 0),
256
- Coordinates.new(0, BOARD_SIZE - 1),
257
- Coordinates.new(BOARD_SIZE - 1, BOARD_SIZE - 1)
258
- ]
259
- corner.include? position
260
- end
261
-
262
- # Überprüft, ob die gegebene [position] schon mit einer Farbe belegt wurde.
78
+ # Überprüft, ob die gegebene [position] mit einem Spielstein belegt ist.
263
79
  # @param board [Board] Das aktuelle Spielbrett
264
80
  # @param position [Coordinates] Die zu überprüfenden Koordinaten
81
+ #
82
+ # @return [Boolean] Ob die position belegt wurde
265
83
  def self.obstructed?(board, position)
266
- !board[position].color.nil?
84
+ !board.field_at(position).empty?
267
85
  end
268
86
 
269
87
  # --- Perform Move ------------------------------------------------------------
@@ -271,66 +89,48 @@ class GameRuleLogic
271
89
  # Führe den gegebenen [Move] im gebenenen [GameState] aus.
272
90
  # @param gamestate [GameState] der aktuelle Spielstand
273
91
  # @param move der auszuführende Zug
92
+ #
93
+ # @return [GameState] Der theoretische GameState
274
94
  def self.perform_move(gamestate, move)
275
95
  raise 'Invalid move!' unless valid_move?(gamestate, move)
276
96
 
277
- if move.instance_of? SetMove
278
- gamestate.undeployed_pieces(move.piece.color).delete(move.piece)
279
- # gamestate.deployed_pieces(move.piece.color).add(move.piece)
97
+ from_field = gamestate.board.field_at(move.from)
98
+ to_field = gamestate.board.field_at(move.to)
280
99
 
281
- # Apply piece to board
282
- move.piece.coords.each do |coord|
283
- gamestate.board[coord].color = move.piece.color
284
- end
100
+ # Update board pieces if one is stepped on
101
+ if not to_field.empty?
102
+ from_field.piece.height = from_field.piece.height + 1
285
103
 
286
- # If it was the last piece for this color, remove it from the turn queue
287
- if gamestate.undeployed_pieces(move.piece.color).empty?
288
- gamestate.lastMoveMono += move.color to(move.piece.kind == PieceShape.MONO)
289
- gamestate.remove_active_color
104
+ # Check for high tower
105
+ if from_field.piece.height >= 3
106
+ gamestate.current_player.amber = gamestate.current_player.amber + 1
107
+ to_field.piece = nil
290
108
  end
291
109
  end
110
+
111
+ # Update board fields
112
+ to_field.piece = from_field.piece
113
+ from_field.piece = nil
114
+
292
115
  gamestate.turn += 1
293
- gamestate.round += 1
294
116
  gamestate.last_move = move
295
117
  end
296
118
 
297
119
  # --- Other ------------------------------------------------------------
298
120
 
299
- # Berechne den Punktestand anhand der gegebenen [PieceShape]s.
300
- # @param undeployed eine Sammlung aller nicht gelegten [PieceShape]s
301
- # @param monoLast ob der letzte gelegte Stein das Monomino war
121
+ # Prueft, ob ein Spieler im gegebenen GameState gewonnen hat.
122
+ # @param gamestate [GameState] Der zu untersuchende GameState.
302
123
  #
303
- # @return die erreichte Punktezahl
304
- def self.get_points_from_undeployed(undeployed, mono_last = false)
305
- # If all pieces were placed:
306
- if undeployed.empty?
307
- # Return sum of all squares plus 15 bonus points
308
- return SUM_MAX_SQUARES + 15 +
309
- # If the Monomino was the last placed piece, add another 5 points
310
- mono_last ? 5 : 0
124
+ # @return [Condition] nil, if the game is not won or a Condition indicating the winning player
125
+ def self.winning_condition(gamestate)
126
+ if gamestate.player_one.amber >= 2
127
+ Condition.new(gamestate.player_one, "Spieler 1 hat 2 Bernsteine erreicht")
311
128
  end
312
- # One point per block per piece placed
313
- SUM_MAX_SQUARES - undeployed.map(&:size).sum
314
- end
315
-
316
- # Gibt einen zufälligen Pentomino zurück, welcher nicht `x` ist.
317
- def self.get_random_pentomino
318
- PieceShape.map(&:value).select { |it| it.size == 5 && it != PieceShape::PENTO_X }
319
- end
320
129
 
321
- # Entferne alle Farben, die keine Steine mehr auf dem Feld platzieren können.
322
- def remove_invalid_colors(gamestate)
323
- return if gamestate.ordered_colors.empty?
324
- return unless get_possible_moves(gamestate).empty?
325
-
326
- gamestate.remove_active_color
327
- remove_invalid_colors(gamestate)
328
- end
130
+ if gamestate.player_two.amber >= 2
131
+ Condition.new(gamestate.player_two, "Spieler 2 hat 2 Bernsteine erreicht")
132
+ end
329
133
 
330
- # Prueft, ob ein Spieler im gegebenen GameState gewonnen hat.
331
- # @param gamestate [GameState] Der zu untersuchende GameState.
332
- # @return [Condition] nil, if the game is not won or a Condition indicating the winning player
333
- def self.winning_condition(_gamestate)
334
- raise 'Not implemented yet!'
134
+ nil
335
135
  end
336
136
  end