software_challenge_client 21.2.0 → 22.1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -1
  3. data/Dockerfile +1 -1
  4. data/README.md +1 -1
  5. data/RELEASES.md +8 -0
  6. data/bin/console +0 -0
  7. data/bin/setup +0 -0
  8. data/develop.sh +0 -0
  9. data/example/main.rb +0 -0
  10. data/example/start.bat +0 -0
  11. data/generate-authors.sh +0 -0
  12. data/lib/software_challenge_client/board.rb +11 -61
  13. data/lib/software_challenge_client/color.rb +13 -3
  14. data/lib/software_challenge_client/field.rb +30 -11
  15. data/lib/software_challenge_client/game_rule_logic.rb +61 -254
  16. data/lib/software_challenge_client/game_state.rb +153 -232
  17. data/lib/software_challenge_client/move.rb +41 -0
  18. data/lib/software_challenge_client/piece.rb +55 -63
  19. data/lib/software_challenge_client/piece_type.rb +16 -0
  20. data/lib/software_challenge_client/player.rb +12 -6
  21. data/lib/software_challenge_client/protocol.rb +201 -266
  22. data/lib/software_challenge_client/runner.rb +1 -1
  23. data/lib/software_challenge_client/team.rb +25 -0
  24. data/lib/software_challenge_client/util/constants.rb +2 -3
  25. data/lib/software_challenge_client/version.rb +1 -1
  26. data/lib/software_challenge_client.rb +3 -6
  27. data/lib/update_client_module.sh +0 -0
  28. data/push_image_production.sh +12 -0
  29. data/release.sh +0 -0
  30. metadata +7 -10
  31. data/lib/software_challenge_client/coordinate_set.rb +0 -92
  32. data/lib/software_challenge_client/piece_shape.rb +0 -109
  33. data/lib/software_challenge_client/player_type.rb +0 -14
  34. data/lib/software_challenge_client/rotation.rb +0 -22
  35. data/lib/software_challenge_client/set_move.rb +0 -24
  36. data/lib/software_challenge_client/skip_move.rb +0 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 679cfef66fc6ad4179d34801147b0640e79e2930c607069c3da6e52c5ff00862
4
- data.tar.gz: bf3dc2f6e06ed94e72328b8babc4741faad2c0510e965f144dcd94b9a80c66ee
3
+ metadata.gz: fb51cb43ad01dbd89c1201d2cfc724131e479c4046421f74f7c437c8a97572ea
4
+ data.tar.gz: 35209a8eee036071d32ad0d397eb0c79d91e3a99785846b2171867687fe048bd
5
5
  SHA512:
6
- metadata.gz: d99e60671de8a35ad5901c46c418bce5b49ab1ef91551cccc17284bbdf82272719a3049dac3338fcd660c48eea21bc2e2ad8262c3d6803da7eab5251e33664ee
7
- data.tar.gz: 2ef1c173e01c84cfdc37cd882aea28e04af7151cad00276b1d4a8d4d3f26243fd0e2f8e01584dd6f007954715b2ac6af5fed961aeb5cc0c2982788df296456eb
6
+ metadata.gz: c363296bfad49e86c6b5394564e1460b50e09899ab673da822497e88b04b4236618767a1ca07342234be5017a9189410514864d249679700b05cfa92ad6676e1
7
+ data.tar.gz: c9bde7fc8db24b18b9c47e8ac5c0b2d60a3d75fa549b442c2e64bd9f9807ab17794d5958dceb3f12c1711821a3c90150402e03748a0e2168499b0bac312c3bcf
data/.gitignore CHANGED
@@ -9,4 +9,7 @@
9
9
  /tmp/
10
10
  /bin/
11
11
  /vendor
12
- /spec/examples.txt
12
+ /spec/examples.txt
13
+ /test/
14
+ *.gem
15
+ test/client.rb
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/README.md CHANGED
@@ -34,7 +34,7 @@ Oder installiere das Gem ohne ein Gemfile mit:
34
34
 
35
35
  ## Verwendung
36
36
 
37
- Ein Beispielprojekt zur Verwendung der Bibliothek findet man im Verzeichnis `example` ([Beipspielprojekt auf GitHub](https://github.com/CAU-Kiel-Tech-Inf/socha_ruby_client/tree/master/example)).
37
+ Ein Beispielprojekt zur Verwendung der Bibliothek findet man im Verzeichnis `example` ([Beispielprojekt auf GitHub](https://github.com/CAU-Kiel-Tech-Inf/socha_ruby_client/tree/master/example)).
38
38
 
39
39
  Du kannst den Beispielclient mittels
40
40
 
data/RELEASES.md CHANGED
@@ -1,3 +1,11 @@
1
+ = 22.1.0
2
+
3
+ Fix gamestate.round
4
+
5
+ = 22.0.3
6
+
7
+ First tagged Version for Osteseeschach
8
+
1
9
  = 21.2.0
2
10
 
3
11
  Adjustments for Backend version 21.2.0
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
@@ -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,250 +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
44
+ # Hilfsmethode um Legezüge für einen [Piece] zu berechnen.
39
45
  # @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.
102
- # @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
- return false if move.piece.color != gamestate.current_color
67
+ return false unless gamestate.board.in_bounds?(move.to)
199
68
 
200
- if gamestate.is_first_move?
201
- # on first turn, only the start piece is allowed
202
- return false if move.piece.kind != gamestate.start_piece
203
- # and it may only be placed in a corner
204
- return false if move.piece.coords.none? { |it| corner?(it) }
205
- else
206
- # in all other turns, only unused pieces may be placed
207
- return false unless gamestate.undeployed_pieces(move.piece.color).include?(move.piece.kind)
208
- # and it needs to be connected to another piece of the same color
209
- return false if move.piece.coords.none? { |it| corners_on_color?(gamestate.board, it, move.piece.color) }
210
- end
69
+ return false if gamestate.board.field_at(move.to).color == move.piece(gamestate).color
211
70
 
212
- # all parts of the piece need to be
213
- move.piece.coords.each do |it|
214
- # - on the board
215
- return false unless gamestate.board.in_bounds?(it)
216
- # - on a empty field
217
- return false if obstructed?(gamestate.board, it)
218
- # - not next to a field occupied by the same color
219
- return false if borders_on_color?(gamestate.board, it, move.piece.color)
220
- end
71
+ return false unless move.piece(gamestate).target_coords.include? move.to
221
72
 
222
- true
223
- end
224
-
225
- # Überprüft, ob das gegebene Feld ein Nachbarfeld mit der Farbe [color] hat
226
- # @param board [Board] Das aktuelle Spielbrett
227
- # @param field [Field] Das zu überprüfende Feld
228
- # @param color [Color] Nach der zu suchenden Farbe
229
- def self.borders_on_color?(board, position, color)
230
- [Coordinates.new(1, 0), Coordinates.new(0, 1), Coordinates.new(-1, 0), Coordinates.new(0, -1)].any? do |it|
231
- if board.in_bounds?(position + it)
232
- board[position + it].color == color
233
- else
234
- false
235
- end
236
- end
237
- end
238
-
239
- # Überprüft, ob das gegebene Feld ein diagonales Nachbarfeld mit der Farbe [color] hat
240
- # @param board [Board] Das aktuelle Spielbrett
241
- # @param position [Field] Das zu überprüfende Feld
242
- # @param color [Color] Nach der zu suchenden Farbe
243
- def self.corners_on_color?(board, position, color)
244
- [Coordinates.new(1, 1), Coordinates.new(1, -1), Coordinates.new(-1, -1), Coordinates.new(-1, 1)].any? do |it|
245
- board.in_bounds?(position + it) && board[position + it].color == color
246
- end
247
- end
73
+ # TODO 2022: Forgot checks?
248
74
 
249
- # Überprüft, ob die gegebene [position] an einer Ecke des Boards liegt.
250
- # @param position [Coordinates] Die zu überprüfenden Koordinaten
251
- def self.corner?(position)
252
- corner = [
253
- Coordinates.new(0, 0),
254
- Coordinates.new(BOARD_SIZE - 1, 0),
255
- Coordinates.new(0, BOARD_SIZE - 1),
256
- Coordinates.new(BOARD_SIZE - 1, BOARD_SIZE - 1)
257
- ]
258
- corner.include? position
75
+ true
259
76
  end
260
77
 
261
- # Überprüft, ob die gegebene [position] schon mit einer Farbe belegt wurde.
78
+ # Überprüft, ob die gegebene [position] mit einem Spielstein belegt ist.
262
79
  # @param board [Board] Das aktuelle Spielbrett
263
80
  # @param position [Coordinates] Die zu überprüfenden Koordinaten
81
+ #
82
+ # @return [Boolean] Ob die position belegt wurde
264
83
  def self.obstructed?(board, position)
265
- !board[position].color.nil?
84
+ !board.field_at(position).empty?
266
85
  end
267
86
 
268
87
  # --- Perform Move ------------------------------------------------------------
@@ -270,65 +89,53 @@ class GameRuleLogic
270
89
  # Führe den gegebenen [Move] im gebenenen [GameState] aus.
271
90
  # @param gamestate [GameState] der aktuelle Spielstand
272
91
  # @param move der auszuführende Zug
92
+ #
93
+ # @return [GameState] Der theoretische GameState
273
94
  def self.perform_move(gamestate, move)
274
95
  raise 'Invalid move!' unless valid_move?(gamestate, move)
275
96
 
276
- if move.instance_of? SetMove
277
- gamestate.undeployed_pieces(move.piece.color).delete(move.piece)
97
+ from_field = gamestate.board.field_at(move.from)
98
+ to_field = gamestate.board.field_at(move.to)
278
99
 
279
- # Apply piece to board
280
- move.piece.coords.each do |coord|
281
- gamestate.board[coord].color = move.piece.color
282
- 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
283
103
 
284
- # If it was the last piece for this color, remove it from the turn queue
285
- if gamestate.undeployed_pieces(move.piece.color).empty?
286
- gamestate.lastMoveMono += move.color to(move.piece.kind == PieceShape.MONO)
287
- 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
288
108
  end
289
109
  end
110
+
111
+ # Update board fields
112
+ to_field.piece = from_field.piece
113
+ from_field.piece = nil
114
+
115
+ # Update position value of the moved piece
116
+ if !to_field.empty? && !to_field.piece.nil?
117
+ to_field.piece.position = Coordinates.new(to_field.coordinates.x, to_field.coordinates.y)
118
+ end
119
+
290
120
  gamestate.turn += 1
291
- gamestate.round += 1
292
121
  gamestate.last_move = move
293
122
  end
294
123
 
295
124
  # --- Other ------------------------------------------------------------
296
125
 
297
- # Berechne den Punktestand anhand der gegebenen [PieceShape]s.
298
- # @param undeployed eine Sammlung aller nicht gelegten [PieceShape]s
299
- # @param monoLast ob der letzte gelegte Stein das Monomino war
126
+ # Prueft, ob ein Spieler im gegebenen GameState gewonnen hat.
127
+ # @param gamestate [GameState] Der zu untersuchende GameState.
300
128
  #
301
- # @return die erreichte Punktezahl
302
- def self.get_points_from_undeployed(undeployed, mono_last = false)
303
- # If all pieces were placed:
304
- if undeployed.empty?
305
- # Return sum of all squares plus 15 bonus points
306
- return SUM_MAX_SQUARES + 15 +
307
- # If the Monomino was the last placed piece, add another 5 points
308
- mono_last ? 5 : 0
129
+ # @return [Condition] nil, if the game is not won or a Condition indicating the winning player
130
+ def self.winning_condition(gamestate)
131
+ if gamestate.player_one.amber >= 2
132
+ Condition.new(gamestate.player_one, "Spieler 1 hat 2 Bernsteine erreicht")
309
133
  end
310
- # One point per block per piece placed
311
- SUM_MAX_SQUARES - undeployed.map(&:size).sum
312
- end
313
-
314
- # Gibt einen zufälligen Pentomino zurück, welcher nicht `x` ist.
315
- def self.get_random_pentomino
316
- PieceShape.map(&:value).select { |it| it.size == 5 && it != PieceShape::PENTO_X }
317
- end
318
134
 
319
- # Entferne alle Farben, die keine Steine mehr auf dem Feld platzieren können.
320
- def remove_invalid_colors(gamestate)
321
- return if gamestate.ordered_colors.empty?
322
- return unless get_possible_moves(gamestate).empty?
323
-
324
- gamestate.remove_active_color
325
- remove_invalid_colors(gamestate)
326
- end
135
+ if gamestate.player_two.amber >= 2
136
+ Condition.new(gamestate.player_two, "Spieler 2 hat 2 Bernsteine erreicht")
137
+ end
327
138
 
328
- # Prueft, ob ein Spieler im gegebenen GameState gewonnen hat.
329
- # @param gamestate [GameState] Der zu untersuchende GameState.
330
- # @return [Condition] nil, if the game is not won or a Condition indicating the winning player
331
- def self.winning_condition(_gamestate)
332
- raise 'Not implemented yet!'
139
+ nil
333
140
  end
334
141
  end