software_challenge_client 20.2.4 → 21.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -0
  3. data/.ruby-version +1 -1
  4. data/.vscode/launch.json +41 -0
  5. data/.vscode/settings.json +10 -0
  6. data/Dockerfile +1 -1
  7. data/Gemfile +1 -0
  8. data/Guardfile +1 -0
  9. data/README.md +4 -3
  10. data/RELEASES.md +4 -0
  11. data/Rakefile +4 -3
  12. data/example/client.rb +6 -9
  13. data/example/main.rb +9 -9
  14. data/lib/software_challenge_client.rb +26 -23
  15. data/lib/software_challenge_client/board.rb +94 -37
  16. data/lib/software_challenge_client/client_interface.rb +1 -0
  17. data/lib/software_challenge_client/color.rb +23 -0
  18. data/lib/software_challenge_client/condition.rb +2 -0
  19. data/lib/software_challenge_client/coordinate_set.rb +92 -0
  20. data/lib/software_challenge_client/coordinates.rb +45 -0
  21. data/lib/software_challenge_client/debug_hint.rb +1 -0
  22. data/lib/software_challenge_client/field.rb +21 -56
  23. data/lib/software_challenge_client/game_rule_logic.rb +255 -332
  24. data/lib/software_challenge_client/game_state.rb +86 -68
  25. data/lib/software_challenge_client/has_hints.rb +1 -1
  26. data/lib/software_challenge_client/invalid_move_exception.rb +1 -0
  27. data/lib/software_challenge_client/logging.rb +1 -0
  28. data/lib/software_challenge_client/network.rb +1 -1
  29. data/lib/software_challenge_client/piece.rb +43 -14
  30. data/lib/software_challenge_client/piece_shape.rb +83 -0
  31. data/lib/software_challenge_client/player.rb +7 -6
  32. data/lib/software_challenge_client/player_type.rb +14 -0
  33. data/lib/software_challenge_client/protocol.rb +81 -75
  34. data/lib/software_challenge_client/rotation.rb +22 -0
  35. data/lib/software_challenge_client/runner.rb +2 -1
  36. data/lib/software_challenge_client/set_move.rb +13 -4
  37. data/lib/software_challenge_client/skip_move.rb +5 -0
  38. data/lib/software_challenge_client/util/constants.rb +3 -4
  39. data/lib/software_challenge_client/version.rb +2 -1
  40. data/lib/update_client_module.sh +15 -0
  41. data/software_challenge_client.gemspec +14 -12
  42. metadata +52 -35
  43. data/lib/software_challenge_client/cube_coordinates.rb +0 -25
  44. data/lib/software_challenge_client/direction.rb +0 -55
  45. data/lib/software_challenge_client/drag_move.rb +0 -22
  46. data/lib/software_challenge_client/line_direction.rb +0 -15
  47. data/lib/software_challenge_client/piece_type.rb +0 -18
  48. data/lib/software_challenge_client/player_color.rb +0 -25
@@ -1,4 +1,3 @@
1
- # encoding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  require_relative './util/constants'
@@ -15,32 +14,49 @@ class GameState
15
14
  # @!attribute [rw] turn
16
15
  # @return [Integer] Aktuelle Zugnummer (von 0 beginnend)
17
16
  attr_accessor :turn
18
- # @!attribute [rw] start_player_color
19
- # @return [PlayerColor] Die Farbe des Spielers, der den ersten Zug im Spiel
20
- # machen darf.
21
- attr_accessor :start_player_color
22
- # @!attribute [rw] current_player_color
23
- # @return [PlayerColor] Die Farbe des Spielers, der den nächsten Zug machen
24
- # darf, der also gerade an der Reihe ist.
25
- attr_accessor :current_player_color
17
+ # @!attribute [rw] round
18
+ # @return [Integer] Aktuelle Rundennummer (von 1 beginnend)
19
+ attr_accessor :round
20
+
21
+ # @!attribute [rw] startColor
22
+ # @return [Color] Die Farbe, die zuerst legen darf
23
+ attr_accessor :start_color
24
+ # @!attribute [rw] current_color_index
25
+ # @return [Color] Der jetzige Index in der Zug Reihenfolge der Farben.
26
+ attr_accessor :current_color_index
27
+ # @!attribute [rw] ordered_colors
28
+ # @return [Array<Color>] Ein Array aller Farben die ziehen können in
29
+ # der Reihenfolge in der sie drankommen
30
+ attr_accessor :ordered_colors
31
+
32
+ # @!attribute [r] undeployed_blue_pieces
33
+ # @return [Array<PieceShape>] Die blauen, nicht gesetzten Spielsteine
34
+ attr_accessor :undeployed_blue_pieces
35
+
36
+ # @!attribute [r] undeployed_yellow_pieces
37
+ # @return [Array<PieceShape>] Die gelben, nicht gesetzten Spielsteine
38
+ attr_accessor :undeployed_yellow_pieces
26
39
 
27
40
  # @!attribute [r] undeployed_red_pieces
28
- # @return [Player] Die nicht gesetzten Spielsteine des roten Spielers
41
+ # @return [Array<PieceShape>] Die roten, nicht gesetzten Spielsteine
29
42
  attr_accessor :undeployed_red_pieces
30
43
 
31
- # @!attribute [r] undeployed_blue_pieces
32
- # @return [Player] Die nicht gesetzten Spielsteine des roten Spielers
33
- attr_accessor :undeployed_blue_pieces
44
+ # @!attribute [r] undeployed_green_pieces
45
+ # @return [Array<PieceShape>] Die grünen, nicht gesetzten Spielsteine
46
+ attr_accessor :undeployed_green_pieces
34
47
 
35
- # @!attribute [r] red
36
- # @return [Player] Der rote Spieler
37
- attr_reader :red
38
- # @!attribute [r] blue
39
- # @return [Player] Der blaue Spieler
40
- attr_reader :blue
48
+ # @!attribute [r] player_one
49
+ # @return [Player] Der erste Spieler
50
+ attr_reader :player_one
51
+ # @!attribute [r] player_two
52
+ # @return [Player] Der zweite Spieler
53
+ attr_reader :player_two
41
54
  # @!attribute [rw] board
42
55
  # @return [Board] Das aktuelle Spielbrett
43
56
  attr_accessor :board
57
+ # @!attribute [rw] startPiece
58
+ # @return [PieceShape] Der Stein, der im ersten Zug von allen Farben gelegt werden muss
59
+ attr_accessor :start_piece
44
60
  # @!attribute [rw] last_move
45
61
  # @return [Move] Der zuletzt gemachte Zug (ist nil vor dem ersten Zug, also
46
62
  # bei turn == 0)
@@ -54,78 +70,75 @@ class GameState
54
70
  def field(x, y)
55
71
  board.field(x, y)
56
72
  end
57
- def self.parse_pieces_string(string, color)
58
- string.chars.map do |c|
59
- case c
60
- when 'Q'
61
- Piece.new(color, PieceType::BEE)
62
- when 'S'
63
- Piece.new(color, PieceType::SPIDER)
64
- when 'G'
65
- Piece.new(color, PieceType::GRASSHOPPER)
66
- when 'B'
67
- Piece.new(color, PieceType::BEETLE)
68
- when 'A'
69
- Piece.new(color, PieceType::ANT)
70
- end
71
- end
72
- end
73
73
 
74
+ # Erstellt einen neuen leeren Spielstand.
74
75
  def initialize
75
- @current_player_color = PlayerColor::RED
76
- @start_player_color = PlayerColor::RED
76
+ @current_color = Color::RED
77
+ @start_color = Color::RED
77
78
  @board = Board.new
78
79
  @turn = 0
79
- @undeployed_red_pieces = GameState.parse_pieces_string(Constants::STARTING_PIECES, PlayerColor::RED)
80
- @undeployed_blue_pieces = GameState.parse_pieces_string(Constants::STARTING_PIECES, PlayerColor::BLUE)
80
+ @undeployed_blue_pieces = PieceShape.to_a
81
+ @undeployed_yellow_pieces = PieceShape.to_a
82
+ @undeployed_red_pieces = PieceShape.to_a
83
+ @undeployed_green_pieces = PieceShape.to_a
84
+ @start_piece = GameRuleLogic.get_random_pentomino
81
85
  end
82
86
 
83
87
  # Fügt einen Spieler zum Spielzustand hinzu.
84
88
  #
85
89
  # @param player [Player] Der hinzuzufügende Spieler.
86
90
  def add_player(player)
87
- if player.color == PlayerColor::RED
88
- @red = player
89
- elsif player.color == PlayerColor::BLUE
90
- @blue = player
91
+ if player.type == PlayerType::ONE
92
+ @player_one = player
93
+ elsif player.type == PlayerType::TWO
94
+ @player_two = player
91
95
  end
92
96
  end
93
97
 
94
98
  # @return [Player] Spieler, der gerade an der Reihe ist.
95
99
  def current_player
96
- return red if current_player_color == PlayerColor::RED
97
- return blue if current_player_color == PlayerColor::BLUE
100
+ turn % 2 == 0 ? player_one : player_two
98
101
  end
99
102
 
100
103
  # @return [Player] Spieler, der gerade nicht an der Reihe ist.
101
104
  def other_player
102
- return blue if current_player_color == PlayerColor::RED
103
- return red if current_player_color == PlayerColor::BLUE
105
+ turn % 2 == 0 ? player_two : player_one
104
106
  end
105
107
 
106
- # @return [PlayerColor] Farbe des Spielers, der gerade nicht an der Reihe ist.
107
- def other_player_color
108
- PlayerColor.opponent_color(current_player_color)
108
+ # @return [PlayerType] Typ des Spielers, der gerade nicht an der Reihe ist.
109
+ def other_player_type
110
+ other_player.type
109
111
  end
110
112
 
111
- # @return [Integer] Aktuelle Runde (von 0 beginnend).
112
- def round
113
- turn / 2
113
+ # @return [Color] Farbe, der gerade an der Reihe ist.
114
+ def current_color
115
+ ordered_colors[current_color_index]
114
116
  end
115
117
 
118
+ # @return [Array<PieceShape>] Array aller Shapes, der gegebenen Farbe, die noch nicht gelegt wurden
116
119
  def undeployed_pieces(color)
117
120
  case color
118
- when PlayerColor::RED
121
+ when Color::RED
119
122
  undeployed_red_pieces
120
- when PlayerColor::BLUE
123
+ when Color::BLUE
121
124
  undeployed_blue_pieces
125
+ when Color::YELLOW
126
+ undeployed_yellow_pieces
127
+ when Color::GREEN
128
+ undeployed_green_pieces
122
129
  end
123
130
  end
124
131
 
132
+ # @return [Array<PieceShape>] Array aller Shapes, der gegebenen Farbe, die schon gelegt wurden
125
133
  def deployed_pieces(color)
126
134
  board.deployed_pieces(color)
127
135
  end
128
136
 
137
+ # @return [Bool] Ob diese gamestate in der ersten Runde ist
138
+ def is_first_move?
139
+ round == 1
140
+ end
141
+
129
142
  # Führt einen Zug auf dem Spielzustand aus. Das Spielbrett wird entsprechend
130
143
  # modifiziert.
131
144
  #
@@ -140,6 +153,11 @@ class GameState
140
153
  !condition.nil?
141
154
  end
142
155
 
156
+ # Entfernt die jetzige Farbe aus der Farbrotation
157
+ def remove_active_color
158
+ ordered_colors.delete current_color
159
+ end
160
+
143
161
  # @return [Player] Der Spieler, der das Spiel gewonnen hat, falls dies schon
144
162
  # entschieden ist. Sonst false.
145
163
  def winner
@@ -156,22 +174,23 @@ class GameState
156
174
  # Rundenlimits beendet wird, hat der Spieler mit den meisten Punkten gewonnen.
157
175
  #
158
176
  # @param player [Player] Der Spieler, dessen Punkte berechnet werden sollen.
159
- # @return [Integer] Die Punkte des Spielers, entspricht der Anzahl der Fische
160
- # im größten Schwarm des Spielers.
161
- def points_for_player(player)
177
+ # @return [Integer] Die Punkte des Spielers
178
+ def points_for_player(_player)
162
179
  # TODO
163
180
  -1
164
181
  end
165
182
 
166
183
  def ==(other)
167
184
  turn == other.turn &&
168
- start_player_color == other.start_player_color &&
169
- current_player_color == other.current_player_color &&
170
- red == other.red &&
171
- blue == other.blue &&
172
- board == other.board &&
173
- lastMove == other.lastMove &&
174
- condition == other.condition
185
+ start_color == other.start_color &&
186
+ current_color == other.current_color &&
187
+ blue == other.blue &&
188
+ yellow == other.yellow &&
189
+ red == other.red &&
190
+ green == other.green &&
191
+ board == other.board &&
192
+ lastMove == other.lastMove &&
193
+ condition == other.condition
175
194
  end
176
195
 
177
196
  # Erzeugt eine Kopie des Spielzustandes. Änderungen an dieser Kopie
@@ -186,9 +205,8 @@ class GameState
186
205
  @current_player_color = other_player_color
187
206
  end
188
207
 
189
- # @return [Array<Field>] Alle Felder mit Fischen des Spielers, der gerade an der Reihe ist.
208
+ # @return [Array<Field>] Alle Felder mit Blöcken des Spielers, der gerade an der Reihe ist.
190
209
  def own_fields
191
- board.fields_of_color(current_player_color)
210
+ board.fields_of_color(current_color)
192
211
  end
193
-
194
212
  end
@@ -1,5 +1,5 @@
1
+ # frozen_string_literal: true
1
2
  module HasHints
2
-
3
3
  # @!attribute [r] hints
4
4
  # @return [Array<DebugHint>] Hinweise, die an den Zug angeheftet werden sollen. Siehe {DebugHint}.
5
5
  attr_reader :hints
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  # Exception, die geworfen wird, wenn ein ungültiger Zug ausgeführt wird.
4
5
  # @see GameRuleLogic#perform_move
@@ -1,4 +1,5 @@
1
1
  # coding: utf-8
2
+ # frozen_string_literal: true
2
3
  require 'logger'
3
4
 
4
5
  # Dieses Modul kann inkludiert werden, um eine Logausgabe auf der Konsole verwenden zu können.
@@ -1,4 +1,5 @@
1
1
  # encoding: UTF-8
2
+ # frozen_string_literal: false
2
3
 
3
4
  require 'socket'
4
5
  require 'rexml/document'
@@ -122,5 +123,4 @@ class Network
122
123
  def emptyReceiveBuffer
123
124
  @receive_buffer = ''
124
125
  end
125
-
126
126
  end
@@ -1,31 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Ein Spielstein mit Ausrichtung, Koordinaten und Farbe
1
4
  class Piece
5
+ # @!attribute [r] Farbe
6
+ # @return [Color]
7
+ attr_reader :color
2
8
 
3
- # @!attribute [r] type
4
- # @return [PieceType]
5
- attr_reader :type
9
+ # @!attribute [r] Form
10
+ # @return [PieceShape]
11
+ attr_reader :kind
6
12
 
7
- # @!attribute [r] color
8
- # @return [PlayerColor]
9
- attr_reader :color
13
+ # @!attribute [r] Drehung
14
+ # @return [Rotation]
15
+ attr_reader :rotation
16
+
17
+ # @!attribute [r] Ob der Stein an der Y-Achse gespiegelt ist
18
+ # @return [Boolean]
19
+ attr_reader :is_flipped
20
+
21
+ # @!attribute [r] Koordinaten
22
+ # @return [Coordinates]
23
+ attr_reader :position
24
+
25
+ attr_reader :coords
10
26
 
11
- def initialize(color, type)
12
- @type = type
27
+ # Erstellt einen neuen leeren Spielstein.
28
+ def initialize(color, kind, rotation = Rotation::NONE, is_flipped = false, position = Coordinates.origin)
13
29
  @color = color
14
- end
30
+ @kind = kind
31
+ @rotation = rotation
32
+ @is_flipped = is_flipped
33
+ @position = position
15
34
 
16
- def ==(other)
17
- type == other.type && color == other.color
35
+ @coords = coords_priv
18
36
  end
19
37
 
20
- def owner
21
- color
38
+ def ==(other)
39
+ color == other.color &&
40
+ kind == other.kind &&
41
+ rotation == other.rotation &&
42
+ is_flipped == other.is_flipped &&
43
+ position == other.position
22
44
  end
23
45
 
24
46
  def to_s
25
- color.value + type.value
47
+ "#{color.key} #{kind.key} at #{position} rotation #{rotation.key}#{is_flipped ? ' (flipped)' : ''}"
26
48
  end
27
49
 
28
50
  def inspect
29
51
  to_s
30
52
  end
53
+
54
+ private
55
+ def coords_priv
56
+ kind.transform(@rotation, @is_flipped).transform do |it|
57
+ Coordinates.new(it.x + @position.x, it.y + @position.y)
58
+ end.coordinates
59
+ end
31
60
  end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'typesafe_enum'
4
+
5
+ require_relative 'coordinates'
6
+ require_relative 'coordinate_set'
7
+
8
+ # Die Form eines Spielsteins. Es gibt folgende Formen:
9
+ #
10
+ # MONO
11
+ # DOMINO
12
+ # TRIO_L
13
+ # TRIO_I
14
+ # TETRO_O
15
+ # TETRO_T
16
+ # TETRO_I
17
+ # TETRO_L
18
+ # TETRO_Z
19
+ # PENTO_L
20
+ # PENTO_T
21
+ # PENTO_V
22
+ # PENTO_S
23
+ # PENTO_Z
24
+ # PENTO_I
25
+ # PENTO_P
26
+ # PENTO_W
27
+ # PENTO_U
28
+ # PENTO_R
29
+ # PENTO_X
30
+ # PENTO_Y
31
+ #
32
+ # Zugriff z.B. mit PieceShape::PENTO_S
33
+ class PieceShape < TypesafeEnum::Base
34
+ def self.c(x, y)
35
+ Coordinates.new(x, y)
36
+ end
37
+ new :MONO, [c(0, 0)]
38
+ new :DOMINO, [c(0, 0), c(1, 0)]
39
+ new :TRIO_L, [c(0, 0), c(0, 1), c(1, 1)]
40
+ new :TRIO_I, [c(0, 0), c(0, 1), c(0, 2)]
41
+ new :TETRO_O, [c(0, 0), c(1, 0), c(0, 1), c(1, 1)]
42
+ new :TETRO_T, [c(0, 0), c(1, 0), c(2, 0), c(1, 1)]
43
+ new :TETRO_I, [c(0, 0), c(0, 1), c(0, 2), c(0, 3)]
44
+ new :TETRO_L, [c(0, 0), c(0, 1), c(0, 2), c(1, 2)]
45
+ new :TETRO_Z, [c(0, 0), c(1, 0), c(1, 1), c(2, 1)]
46
+ new :PENTO_L, [c(0, 0), c(0, 1), c(0, 2), c(0, 3), c(1, 3)]
47
+ new :PENTO_T, [c(0, 0), c(1, 0), c(2, 0), c(1, 1), c(1, 2)]
48
+ new :PENTO_V, [c(0, 0), c(0, 1), c(0, 2), c(1, 2), c(2, 2)]
49
+ new :PENTO_S, [c(1, 0), c(2, 0), c(3, 0), c(0, 1), c(1, 1)]
50
+ new :PENTO_Z, [c(0, 0), c(1, 0), c(1, 1), c(1, 2), c(2, 2)]
51
+ new :PENTO_I, [c(0, 0), c(0, 1), c(0, 2), c(0, 3), c(0, 4)]
52
+ new :PENTO_P, [c(0, 0), c(1, 0), c(0, 1), c(1, 1), c(0, 2)]
53
+ new :PENTO_W, [c(0, 0), c(0, 1), c(1, 1), c(1, 2), c(2, 2)]
54
+ new :PENTO_U, [c(0, 0), c(0, 1), c(1, 1), c(2, 1), c(2, 0)]
55
+ new :PENTO_R, [c(0, 1), c(1, 1), c(1, 2), c(2, 1), c(2, 0)]
56
+ new :PENTO_X, [c(1, 0), c(0, 1), c(1, 1), c(2, 1), c(1, 2)]
57
+ new :PENTO_Y, [c(0, 1), c(1, 0), c(1, 1), c(1, 2), c(1, 3)]
58
+
59
+ # Anzahl Felder, die der Stein belegt
60
+ def size
61
+ value.size
62
+ end
63
+
64
+ # Die Felder, die der Stein belegt
65
+ def coordinates
66
+ CoordinateSet.new(value)
67
+ end
68
+
69
+ # Eine Koordinate, die das kleinstmögliche Rechteck beschreibt, welches alle Felder umfasst.
70
+ def dimension
71
+ coordinates.area
72
+ end
73
+
74
+ # Erzeugt eine nach Rotation und Flip transformierte Form
75
+ def transform(rotation, flip)
76
+ coordinates.rotate(rotation).flip(flip)
77
+ end
78
+
79
+ # Gibt den Form Namen zurück
80
+ def to_s
81
+ self.key.to_s
82
+ end
83
+ end
@@ -1,4 +1,5 @@
1
1
  # encoding: UTF-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  # Ein Spieler
4
5
  class Player
@@ -6,15 +7,15 @@ class Player
6
7
  # @return [String] der Name des Spielers, hat keine Auswirkungen auf das Spiel
7
8
  attr_reader :name
8
9
 
9
- # @!attribute [r] color
10
- # @return [PlayerColor] die Farbe des Spielers, Rot oder Blau
11
- attr_reader :color
10
+ # @!attribute [r] type
11
+ # @return [PlayerType] erster (PlayerType::ONE) oder zweiter (PlayerType::TWO) Spieler
12
+ attr_reader :type
12
13
 
13
14
  # Konstruktor
14
- # @param color [PlayerColor] Farbe
15
+ # @param type [PlayerType] Erster oder zweiter
15
16
  # @param name [String] Name
16
- def initialize(color, name)
17
- @color = color
17
+ def initialize(type, name)
18
+ @type = type
18
19
  @name = name
19
20
  end
20
21