software_challenge_client 21.2.0 → 22.1.0.1

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.
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