software_challenge_client 21.2.0 → 22.0.2

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.
@@ -4,6 +4,7 @@ require_relative './util/constants'
4
4
  require_relative 'player'
5
5
  require_relative 'board'
6
6
  require_relative 'condition'
7
+ require_relative 'color'
7
8
 
8
9
  # Ein Spielzustand. Wird vom Server an die Computerspieler übermittelt und
9
10
  # enthält alles, was der Computerspieler wissen muss, um einen Zug zu machen.
@@ -19,36 +20,6 @@ class GameState
19
20
  # @return [Integer] Aktuelle Rundennummer (von 1 beginnend)
20
21
  attr_accessor :round
21
22
 
22
- # @!attribute [rw] startColor
23
- # @return [Color] Die Farbe, die zuerst legen darf
24
- attr_accessor :start_color
25
-
26
- # @!attribute [rw] valid_colors
27
- # @return [Array<Color>] Ein Array aller Farben die ziehen können in
28
- # der Reihenfolge in der sie drankommen
29
- attr_accessor :valid_colors
30
-
31
- # @!attribute [rw] ordered_colors
32
- # @return [Array<Color>] Ein Array aller Farben in
33
- # der Reihenfolge in der sie drankommen
34
- attr_accessor :ordered_colors
35
-
36
- # @!attribute [r] undeployed_blue_pieces
37
- # @return [Array<PieceShape>] Die blauen, nicht gesetzten Spielsteine
38
- attr_accessor :undeployed_blue_pieces
39
-
40
- # @!attribute [r] undeployed_yellow_pieces
41
- # @return [Array<PieceShape>] Die gelben, nicht gesetzten Spielsteine
42
- attr_accessor :undeployed_yellow_pieces
43
-
44
- # @!attribute [r] undeployed_red_pieces
45
- # @return [Array<PieceShape>] Die roten, nicht gesetzten Spielsteine
46
- attr_accessor :undeployed_red_pieces
47
-
48
- # @!attribute [r] undeployed_green_pieces
49
- # @return [Array<PieceShape>] Die grünen, nicht gesetzten Spielsteine
50
- attr_accessor :undeployed_green_pieces
51
-
52
23
  # @!attribute [r] player_one
53
24
  # @return [Player] Der erste Spieler
54
25
  attr_reader :player_one
@@ -57,14 +28,14 @@ class GameState
57
28
  # @return [Player] Der zweite Spieler
58
29
  attr_reader :player_two
59
30
 
31
+ # @!attribute [rw] start_team
32
+ # @return [Team] Der Spieler der zuerst zieht
33
+ attr_accessor :start_team
34
+
60
35
  # @!attribute [rw] board
61
36
  # @return [Board] Das aktuelle Spielbrett
62
37
  attr_accessor :board
63
38
 
64
- # @!attribute [rw] startPiece
65
- # @return [PieceShape] Der Stein, der im ersten Zug von allen Farben gelegt werden muss
66
- attr_accessor :start_piece
67
-
68
39
  # @!attribute [rw] last_move
69
40
  # @return [Move] Der zuletzt gemachte Zug (ist nil vor dem ersten Zug, also
70
41
  # bei turn == 0)
@@ -82,25 +53,18 @@ class GameState
82
53
 
83
54
  # Erstellt einen neuen leeren Spielstand.
84
55
  def initialize
85
- @ordered_colors = [Color::BLUE, Color::YELLOW, Color::RED, Color::GREEN]
86
56
  @board = Board.new
87
57
  @turn = 0
88
- @undeployed_blue_pieces = PieceShape.to_a
89
- @undeployed_yellow_pieces = PieceShape.to_a
90
- @undeployed_red_pieces = PieceShape.to_a
91
- @undeployed_green_pieces = PieceShape.to_a
92
- @start_piece = GameRuleLogic.get_random_pentomino
93
- @start_color = Color::BLUE
94
58
  end
95
59
 
96
60
  # Fügt einen Spieler zum Spielzustand hinzu.
97
61
  #
98
62
  # @param player [Player] Der hinzuzufügende Spieler.
99
63
  def add_player(player)
100
- case player.type
101
- when PlayerType::ONE
64
+ case player.color
65
+ when Color::RED
102
66
  @player_one = player
103
- when PlayerType::TWO
67
+ when Color::BLUE
104
68
  @player_two = player
105
69
  end
106
70
  end
@@ -115,45 +79,11 @@ class GameState
115
79
  turn.even? ? player_two : player_one
116
80
  end
117
81
 
118
- # @return [PlayerType] Typ des Spielers, der gerade nicht an der Reihe ist.
119
- def other_player_type
82
+ # @return [Team] Typ des Spielers, der gerade nicht an der Reihe ist.
83
+ def other_team
120
84
  other_player.type
121
85
  end
122
86
 
123
- # @return [Color] Der jetzige Index in der Zug Reihenfolge der Farben.
124
- def current_color_index
125
- turn % 4
126
- end
127
-
128
- # @return [Color] Farbe, der gerade an der Reihe ist.
129
- def current_color
130
- ordered_colors[current_color_index]
131
- end
132
-
133
- # @return [Color] Farbe des aktuellen Spielers, die gerade nicht an der Reihe ist.
134
- def other_color
135
- Color.find_by_ord((current_color.ord + 2) % 4)
136
- end
137
-
138
- # @return [Array<PieceShape>] Array aller Shapes, der gegebenen Farbe, die noch nicht gelegt wurden
139
- def undeployed_pieces(color)
140
- case color
141
- when Color::RED
142
- undeployed_red_pieces
143
- when Color::BLUE
144
- undeployed_blue_pieces
145
- when Color::YELLOW
146
- undeployed_yellow_pieces
147
- when Color::GREEN
148
- undeployed_green_pieces
149
- end
150
- end
151
-
152
- # @return [Array<PieceShape>] Array aller Shapes, der gegebenen Farbe, die schon gelegt wurden
153
- def deployed_pieces(color)
154
- board.deployed_pieces(color)
155
- end
156
-
157
87
  # @return [Bool] Ob diese gamestate in der ersten Runde ist
158
88
  def is_first_move?
159
89
  round == 1
@@ -173,11 +103,6 @@ class GameState
173
103
  !condition.nil?
174
104
  end
175
105
 
176
- # Entfernt die jetzige Farbe aus der Farbrotation
177
- def remove_active_color
178
- ordered_colors.delete current_color
179
- end
180
-
181
106
  # @return [Player] Der Spieler, der das Spiel gewonnen hat, falls dies schon
182
107
  # entschieden ist. Sonst false.
183
108
  def winner
@@ -227,6 +152,6 @@ class GameState
227
152
 
228
153
  # @return [Array<Field>] Alle Felder mit Blöcken des Spielers, der gerade an der Reihe ist.
229
154
  def own_fields
230
- board.fields_of_color(current_color)
155
+ board.fields_of_color(current_player.color)
231
156
  end
232
157
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'has_hints'
4
+
5
+ # Ein Move repräsentiert eine Bewegung eines Steins auf dem Spielbrett
6
+ class Move
7
+ include HasHints
8
+
9
+ # @!attribute [r] Koordinaten von dem der Spielstein in diesem Zug wegbewegt wird
10
+ # @return [Coordinates]
11
+ attr_reader :from
12
+
13
+ # @!attribute [r] Koordinaten zu denen der Spielstein in diesem Zug hinbewegt wird
14
+ # @return [Coordinates]
15
+ attr_reader :to
16
+
17
+ # Erstellt ein neuen Zug.
18
+ def initialize(from, to)
19
+ @from = from
20
+ @to = to
21
+ @hints = []
22
+ end
23
+
24
+ def piece(gamestate)
25
+ gamestate.board.field_at(from).piece
26
+ end
27
+
28
+ def piece_t(gamestate)
29
+ gamestate.board.field_at(to).piece
30
+ end
31
+
32
+ def ==(other)
33
+ from == other.from &&
34
+ to == other.to
35
+ end
36
+
37
+ # @return [String] Gibt die String-Repräsentation zurück
38
+ def to_s
39
+ "Move(#{from}->#{to})"
40
+ end
41
+ end
@@ -2,88 +2,77 @@
2
2
 
3
3
  # Ein Spielstein mit Ausrichtung, Koordinaten und Farbe
4
4
  class Piece
5
- # @!attribute [r] Farbe
5
+ # @!attribute [rw] Color
6
6
  # @return [Color]
7
- attr_reader :color
7
+ attr_accessor :color
8
8
 
9
- # @!attribute [r] Form
10
- # @return [PieceShape]
11
- attr_reader :kind
9
+ # @!attribute [r] Typ des Spielsteins
10
+ # @return [PieceType]
11
+ attr_reader :type
12
12
 
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
13
+ # @!attribute [rw] Koordinaten
22
14
  # @return [Coordinates]
23
- attr_reader :position
15
+ attr_accessor :position
24
16
 
25
- # @!attribute [r] Ein Array der Positionsdaten aller Bestandteile von dem Stein in Board Koordinaten, also schon ggf. gedreht und um position versetzt.
26
- # return [Array<Coordinates>]
27
- attr_reader :coords
17
+ # @!attribute [rw] tower_height
18
+ # @return [Integer] Die Anzahl Spielsteine übereinander inklusive des obersten
19
+ attr_accessor :height
28
20
 
29
- # Erstellt einen neuen leeren Spielstein.
30
- def initialize(color, kind, rotation = Rotation::NONE, is_flipped = false, position = Coordinates.origin)
21
+ # Erstellt einen neuen Spielstein.
22
+ def initialize(color, type, position = Coordinates.origin, height = 0)
31
23
  @color = color
32
- @kind = kind
33
- @rotation = rotation
34
- @is_flipped = is_flipped
24
+ @type = type
35
25
  @position = position
36
-
37
- @coords = coords_priv
38
- end
39
-
40
- # Dreht den Stein
41
- def rotate!(rotation)
42
- @rotation = @rotation.rotate(rotation)
43
- @coords = coords_priv
26
+ @height = height
44
27
  end
45
28
 
46
- # Flipped den Stein
47
- def flip!(f = true)
48
- @is_flipped = @is_flipped ^ f
49
- @coords = coords_priv
50
- end
51
-
52
- # Setzt den Stein auf eine Position
53
- def locate!(position)
54
- @position = position
55
- @coords = coords_priv
56
- end
57
-
58
- # Verschiebt den Stein
59
- def move!(shift)
60
- @position = position + shift
61
- @coords = coords_priv
62
- end
63
-
64
- # Gibt die Fläche der transformierten Steinform von diesem Stein zurück
65
- def area()
66
- CoordinateSet.new(coords).area
29
+ # Berechnet die Koordinaten zu denen sich dieser Spielstein bewegen könnte.
30
+ #
31
+ # @return [Array<Coordinates>] Die Zielkoordinaten
32
+ def target_coords
33
+ xdir = 0
34
+ if color == Color::RED
35
+ xdir = 1
36
+ else
37
+ xdir = -1
38
+ end
39
+
40
+ case type
41
+ when PieceType::Herzmuschel
42
+ coords = [Coordinates.new(xdir,-1), Coordinates.new(xdir,1)]
43
+ when PieceType::Moewe
44
+ coords = [Coordinates.new(1,0), Coordinates.new(-1,0), Coordinates.new(0,1),
45
+ Coordinates.new(0,-1)]
46
+ when PieceType::Seestern
47
+ coords = [Coordinates.new(xdir,0), Coordinates.new(1,1), Coordinates.new(-1,1),
48
+ Coordinates.new(1,-1), Coordinates.new(-1,-1)]
49
+ when PieceType::Robbe
50
+ coords = [Coordinates.new(-1,2), Coordinates.new(1,2), Coordinates.new(-2,1),
51
+ Coordinates.new(2,1), Coordinates.new(-1,-2), Coordinates.new(1,-2),
52
+ Coordinates.new(-2,-1), Coordinates.new(2,-1)]
53
+ end
54
+
55
+ coords.map{ |x| x + position }.to_a
67
56
  end
68
57
 
69
58
  def ==(other)
70
- color == other.color &&
71
- coords == other.coords
59
+ !other.nil? &&
60
+ color == other.color &&
61
+ position == other.position &&
62
+ type == other.type
72
63
  end
73
64
 
65
+ # @return [String] Gibt die String-Repräsentation zurück
74
66
  def to_s
75
- "#{color.key} #{kind.key} at #{position} rotation #{rotation.key}#{is_flipped ? ' (flipped)' : ''}"
67
+ "#{color.key} #{type.key} at #{position}"
76
68
  end
77
69
 
78
- def inspect
79
- to_s
70
+ # @return [String] Gibt eine Kurzfassung der String-Repräsentation zurück
71
+ def to_ss
72
+ "#{color.key.to_s[0]}#{type.key.to_s[0]}"
80
73
  end
81
74
 
82
- private
83
-
84
- def coords_priv
85
- kind.transform(@rotation, @is_flipped).transform do |it|
86
- Coordinates.new(it.x + @position.x, it.y + @position.y)
87
- end.coordinates
75
+ def inspect
76
+ to_s
88
77
  end
89
78
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'typesafe_enum'
4
+
5
+ # Die Spielsteintypen. Herzmuschel, Möwe, Seestern und Robbe
6
+ class PieceType < TypesafeEnum::Base
7
+ new :Herzmuschel, 'C'
8
+ new :Moewe, 'G'
9
+ new :Seestern, 'S'
10
+ new :Robbe, 'R'
11
+
12
+ # @return [String] Gibt den color namen zurück
13
+ def to_s
14
+ self.key.to_s
15
+ end
16
+ end
@@ -7,16 +7,22 @@ class Player
7
7
  # @return [String] der Name des Spielers, hat keine Auswirkungen auf das Spiel
8
8
  attr_reader :name
9
9
 
10
- # @!attribute [r] type
11
- # @return [PlayerType] erster (PlayerType::ONE) oder zweiter (PlayerType::TWO) Spieler
12
- attr_reader :type
10
+ # @!attribute [r] color
11
+ # @return [Color] erster (Color::RED) oder zweiter (Color::BLUE) Spieler
12
+ attr_reader :color
13
+
14
+ # @!attribute [rw] amber
15
+ # @return [Integer] Anzahl Bernsteine die dieser Spieler gesammelt hat
16
+ attr_accessor :amber
13
17
 
14
18
  # Konstruktor
15
- # @param type [PlayerType] Erster oder zweiter
19
+ # @param type [Color] Rot oder blau
16
20
  # @param name [String] Name
17
- def initialize(type, name)
18
- @type = type
21
+ # @param amber [Integer] Menge des Bernsteins die der Spieler hat
22
+ def initialize(color, name, amber = 0)
23
+ @color = color
19
24
  @name = name
25
+ @amber = amber
20
26
  end
21
27
 
22
28
  def ==(other)
@@ -2,8 +2,8 @@
2
2
  # frozen_string_literal: true
3
3
  require 'socket'
4
4
  require_relative 'board'
5
- require_relative 'set_move'
6
- require_relative 'skip_move'
5
+ require_relative 'move'
6
+ require_relative 'piece_type'
7
7
  require_relative 'player'
8
8
  require_relative 'network'
9
9
  require_relative 'client_interface'
@@ -61,26 +61,23 @@ class Protocol
61
61
  case name
62
62
  when 'board'
63
63
  logger.debug @gamestate.board.to_s
64
- when 'color'
65
- if @context[:color] == :valid_colors
66
- @gamestate.valid_colors << Color.to_a.find {|s| s.key == @context[:last_text].to_sym }
64
+ when 'startTeam'
65
+ @gamestate.add_player(Player.new(Color::RED, "ONE", 0))
66
+ @gamestate.add_player(Player.new(Color::BLUE, "TWO", 0))
67
+ if @context[:last_text] == "ONE"
68
+ @gamestate.start_team = @gamestate.player_one
69
+ else
70
+ @gamestate.start_team = @gamestate.player_two
67
71
  end
68
- when 'shape'
69
- case @context[:piece_target]
70
- when :blue_shapes
71
- last = @context[:last_text]
72
- arr = PieceShape.to_a
73
- shape = arr.find {|s| s.key == @context[:last_text].to_sym }
74
- @gamestate.undeployed_blue_pieces << shape
75
- when :yellow_shapes
76
- shape = PieceShape.to_a.find {|s| s.key == @context[:last_text].to_sym }
77
- @gamestate.undeployed_yellow_pieces << shape
78
- when :red_shapes
79
- shape = PieceShape.to_a.find {|s| s.key == @context[:last_text].to_sym }
80
- @gamestate.undeployed_red_pieces << shape
81
- when :green_shapes
82
- shape = PieceShape.to_a.find {|s| s.key == @context[:last_text].to_sym }
83
- @gamestate.undeployed_green_pieces << shape
72
+ when 'team'
73
+ @context[:team] = @context[:last_text]
74
+ when 'int'
75
+ if @context[:team] == "ONE"
76
+ logger.info 'Got player one amber'
77
+ @gamestate.player_one.amber = @context[:last_text].to_i
78
+ else
79
+ logger.info 'Got player two amber'
80
+ @gamestate.player_two.amber = @context[:last_text].to_i
84
81
  end
85
82
  end
86
83
  end
@@ -99,7 +96,7 @@ class Protocol
99
96
  when 'data'
100
97
  logger.debug "data(class) : #{attrs['class']}"
101
98
  @context[:data_class] = attrs['class']
102
- if attrs['class'] == 'sc.framework.plugins.protocol.MoveRequest'
99
+ if attrs['class'] == 'moveRequest'
103
100
  @client.gamestate = gamestate
104
101
  move = @client.move_requested
105
102
  sendString(move_to_xml(move))
@@ -117,85 +114,31 @@ class Protocol
117
114
  logger.debug 'new gamestate'
118
115
  @gamestate = GameState.new
119
116
  @gamestate.turn = attrs['turn'].to_i
120
- @gamestate.round = attrs['round'].to_i
121
- @gamestate.start_piece = PieceShape.to_a.find {|s| s.key == attrs['startPiece'].to_sym }
117
+ @gamestate.round = @gamestate.turn / 2
122
118
  logger.debug "Round: #{@gamestate.round}, Turn: #{@gamestate.turn}"
123
- when 'first'
124
- logger.debug 'new first player'
125
- player = Player.new(PlayerType::ONE, attrs['displayName'])
126
- @gamestate.add_player(player)
127
- @context[:player] = player
128
- @context[:color] = :one
129
- when 'second'
130
- logger.debug 'new second player'
131
- player = Player.new(PlayerType::TWO, attrs['displayName'])
132
- @gamestate.add_player(player)
133
- @context[:player] = player
134
- @context[:color] = :two
135
- when 'validColors'
136
- @context[:color] = :valid_colors
137
- @gamestate.valid_colors = []
138
119
  when 'board'
139
120
  logger.debug 'new board'
140
121
  @gamestate.board = Board.new()
141
- when 'field'
142
- x = attrs['x'].to_i
143
- y = attrs['y'].to_i
144
- color = Color.find_by_key(attrs['content'].to_sym)
145
- field = Field.new(x, y, color)
146
- @gamestate.board.add_field(field)
147
- @context[:piece_target] = :field
148
- @context[:field] = field
149
- when 'blueShapes'
150
- @context[:piece_target] = :blue_shapes
151
- @gamestate.undeployed_blue_pieces = []
152
- when 'yellowShapes'
153
- @context[:piece_target] = :yellow_shapes
154
- @gamestate.undeployed_yellow_pieces = []
155
- when 'redShapes'
156
- @context[:piece_target] = :red_shapes
157
- @gamestate.undeployed_red_pieces = []
158
- when 'greenShapes'
159
- @context[:piece_target] = :green_shapes
160
- @gamestate.undeployed_green_pieces = []
122
+ when 'pieces'
123
+ @context[:entry] = :pieces
124
+ when 'coordinates'
125
+ @context[:x] = attrs['x'].to_i
126
+ @context[:y] = attrs['y'].to_i
161
127
  when 'piece'
162
- color = Color.find_by_key(attrs['color'].to_sym)
163
- kind = PieceShape.find_by_key(attrs['kind'].to_sym)
164
- rotation = Rotation.find_by_key(attrs['rotation'].to_sym)
165
- is_flipped = attrs['isFlipped'].downcase == "true"
166
- piece = Piece.new(color, kind, rotation, is_flipped, Coordinates.origin)
167
- case @context[:piece_target]
168
- when :blue_shapes
169
- @gamestate.undeployed_blue_pieces << piece
170
- when :yellow_shapes
171
- @gamestate.undeployed_yellow_pieces << piece
172
- when :red_shapes
173
- @gamestate.green_red_pieces << piece
174
- when :green_shapes
175
- @gamestate.undeployed_green_pieces << piece
176
- when :last_move
177
- @context[:last_move_piece] = piece
178
- else
179
- raise "unknown piece target #{@context[:piece_target]}"
180
- end
181
- when 'lastMove'
182
- type = attrs['class']
183
- if type == 'skipmove'
184
- @gamestate.last_move = SkipMove.new
185
- else
186
- @context[:last_move_type] = type
187
- @context[:piece_target] = :last_move
188
- end
189
- when 'position'
190
- case @context[:piece_target]
191
- when :last_move
192
- x = attrs['x'].to_i
193
- y = attrs['y'].to_i
194
- piece = @context[:last_move_piece]
195
- @gamestate.last_move = SetMove.new(Piece.new(piece.color, piece.kind, piece.rotation, piece.is_flipped, Coordinates.new(x, y)))
196
- end
197
- when 'startColor'
198
- @gamestate.start_color = Color::BLUE
128
+ x = @context[:x]
129
+ y = @context[:y]
130
+ team = Team.find_by_key(attrs['team'].to_sym)
131
+ type = PieceType.find_by_key(attrs['type'].to_sym)
132
+ count = attrs['count'].to_i
133
+ field = Field.new(x, y, Piece.new(team.to_c, type, Coordinates.new(x, y), count))
134
+ @gamestate.board.add_field(field)
135
+ when 'from'
136
+ @context[:from] = Coordinates.new(attrs['x'].to_i, attrs['y'].to_i)
137
+ when 'to'
138
+ from = @context[:from]
139
+ @gamestate.last_move = Move.new(Coordinates.new(from.x, from.y), Coordinates.new(attrs['x'].to_i, attrs['y'].to_i))
140
+ when 'ambers'
141
+ @context[:entry] = :ambers
199
142
  when 'winner'
200
143
  # TODO
201
144
  # winning_player = parsePlayer(attrs)
@@ -239,28 +182,21 @@ class Protocol
239
182
  # @param move [Move] The move to convert to XML.
240
183
  def move_to_xml(move)
241
184
  builder = Builder::XmlMarkup.new(indent: 2)
185
+
186
+ if move.nil?
187
+ raise 'nil moves are not sendable!'
188
+ end
189
+
242
190
  # Converting every the move here instead of requiring the Move
243
191
  # class interface to supply a method which returns the XML
244
192
  # because XML-generation should be decoupled from internal data
245
193
  # structures.
246
- case move
247
- when SetMove
248
- builder.data(class: 'sc.plugin2021.SetMove') do |data|
249
- data.piece(color: move.piece.color, kind: move.piece.kind, rotation: move.piece.rotation, isFlipped: move.piece.is_flipped) do |piece|
250
- piece.position(x: move.piece.position.x, y: move.piece.position.y)
251
- end
252
- move.hints.each do |hint|
253
- data.hint(content: hint.content)
254
- end
255
- end
256
- when SkipMove
257
- builder.data(class: 'sc.plugin2021.SkipMove') do |data|
258
- data.color(@gamestate.current_color.key.to_s)
259
- move.hints.each do |hint|
260
- data.hint(content: hint.content)
261
- end
262
- end
194
+
195
+ builder.data(class: 'move') do |d|
196
+ d.from(x: move.from.x, y: move.from.y)
197
+ d.to(x: move.to.x, y: move.to.y)
263
198
  end
199
+
264
200
  builder.target!
265
201
  end
266
202
  end