software_challenge_client 19.1.0 → 20.2.0

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,9 +4,7 @@
4
4
  require_relative './util/constants'
5
5
  require_relative 'player'
6
6
  require_relative 'board'
7
- require_relative 'move'
8
7
  require_relative 'condition'
9
- require_relative 'field_type'
10
8
 
11
9
  # Ein Spielzustand. Wird vom Server an die Computerspieler übermittelt und
12
10
  # enthält alles, was der Computerspieler wissen muss, um einen Zug zu machen.
@@ -25,6 +23,15 @@ class GameState
25
23
  # @return [PlayerColor] Die Farbe des Spielers, der den nächsten Zug machen
26
24
  # darf, der also gerade an der Reihe ist.
27
25
  attr_accessor :current_player_color
26
+
27
+ # @!attribute [r] undeployed_red_pieces
28
+ # @return [Player] Die nicht gesetzten Spielsteine des roten Spielers
29
+ attr_accessor :undeployed_red_pieces
30
+
31
+ # @!attribute [r] undeployed_blue_pieces
32
+ # @return [Player] Die nicht gesetzten Spielsteine des roten Spielers
33
+ attr_accessor :undeployed_blue_pieces
34
+
28
35
  # @!attribute [r] red
29
36
  # @return [Player] Der rote Spieler
30
37
  attr_reader :red
@@ -47,13 +54,30 @@ class GameState
47
54
  def field(x, y)
48
55
  board.field(x, y)
49
56
  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
50
73
 
51
- # Erstellt einen neuen Spielzustand.
52
74
  def initialize
53
75
  @current_player_color = PlayerColor::RED
54
76
  @start_player_color = PlayerColor::RED
55
77
  @board = Board.new
56
78
  @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)
57
81
  end
58
82
 
59
83
  # Fügt einen Spieler zum Spielzustand hinzu.
@@ -89,6 +113,19 @@ class GameState
89
113
  turn / 2
90
114
  end
91
115
 
116
+ def undeployed_pieces(color)
117
+ case color
118
+ when PlayerColor::RED
119
+ undeployed_red_pieces
120
+ when PlayerColor::BLUE
121
+ undeployed_blue_pieces
122
+ end
123
+ end
124
+
125
+ def deployed_pieces(color)
126
+ board.deployed_pieces(color)
127
+ end
128
+
92
129
  # Führt einen Zug auf dem Spielzustand aus. Das Spielbrett wird entsprechend
93
130
  # modifiziert.
94
131
  #
@@ -122,7 +159,8 @@ class GameState
122
159
  # @return [Integer] Die Punkte des Spielers, entspricht der Anzahl der Fische
123
160
  # im größten Schwarm des Spielers.
124
161
  def points_for_player(player)
125
- GameRuleLogic.swarm_size(board, player)
162
+ # TODO
163
+ -1
126
164
  end
127
165
 
128
166
  def ==(other)
@@ -139,7 +177,7 @@ class GameState
139
177
  # Erzeugt eine Kopie des Spielzustandes. Änderungen an dieser Kopie
140
178
  # beeinflussen den originalen Spielzustand nicht. Die Kopie kann also zum
141
179
  # testen von Spielzügen genutzt werden.
142
- def deep_clone
180
+ def clone
143
181
  Marshal.load(Marshal.dump(self))
144
182
  end
145
183
 
@@ -150,16 +188,7 @@ class GameState
150
188
 
151
189
  # @return [Array<Field>] Alle Felder mit Fischen des Spielers, der gerade an der Reihe ist.
152
190
  def own_fields
153
- board.fields_of_type(
154
- PlayerColor.field_type(current_player_color)
155
- )
156
- end
157
-
158
- # @return [Array<Move>] Alle regelkonformen Züge, die der aktuelle Spieler gerade machen könnte.
159
- def possible_moves
160
- own_fields.map do |f|
161
- GameRuleLogic.possible_moves(board, f, current_player_color)
162
- end.flatten
191
+ board.fields_of_color(current_player_color)
163
192
  end
164
193
 
165
194
  end
@@ -0,0 +1,11 @@
1
+ module HasHints
2
+
3
+ # @!attribute [r] hints
4
+ # @return [Array<DebugHint>] Hinweise, die an den Zug angeheftet werden sollen. Siehe {DebugHint}.
5
+ attr_reader :hints
6
+
7
+ # @param hint [DebugHint]
8
+ def add_hint(hint)
9
+ @hints.push(hint)
10
+ end
11
+ end
@@ -1,14 +1,14 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  # Exception, die geworfen wird, wenn ein ungültiger Zug ausgeführt wird.
4
- # @see Move#perform!
4
+ # @see GameRuleLogic#perform_move
5
5
  class InvalidMoveException < StandardError
6
6
  def initialize(msg, move)
7
7
  @move = move
8
8
  super(msg)
9
9
  end
10
10
 
11
- def message
11
+ def to_s
12
12
  "#{super}: #{@move}"
13
13
  end
14
14
  end
@@ -68,14 +68,12 @@ class Network
68
68
  sock_msg = ''
69
69
 
70
70
  line = ''
71
- logger.debug 'reading'
72
71
  @socket.each_char do |char|
73
72
  line += char
74
73
  sock_msg += char
75
74
  line = '' if ['\n', ' '].include? char
76
75
  break if ['</room>', '</protocol>'].include? line
77
76
  end
78
- logger.debug 'ended reading'
79
77
  if sock_msg != ''
80
78
  @receive_buffer.concat(sock_msg)
81
79
 
@@ -0,0 +1,31 @@
1
+ class Piece
2
+
3
+ # @!attribute [r] type
4
+ # @return [PieceType]
5
+ attr_reader :type
6
+
7
+ # @!attribute [r] color
8
+ # @return [PlayerColor]
9
+ attr_reader :color
10
+
11
+ def initialize(color, type)
12
+ @type = type
13
+ @color = color
14
+ end
15
+
16
+ def ==(other)
17
+ type == other.type && color == other.color
18
+ end
19
+
20
+ def owner
21
+ color
22
+ end
23
+
24
+ def to_s
25
+ color.value + type.value
26
+ end
27
+
28
+ def inspect
29
+ to_s
30
+ end
31
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+
3
+ require 'typesafe_enum'
4
+ # Der Typ eines Spielsteins. Es gibt folgende Typen:
5
+ # - BEE
6
+ # - BEETLE
7
+ # - GRASSHOPPER
8
+ # - SPIDER
9
+ # - ANT
10
+ #
11
+ # Zugriff z.B. mit PieceType::BEE
12
+ class PieceType < TypesafeEnum::Base
13
+ new :BEE, 'Q'
14
+ new :BEETLE, 'B'
15
+ new :GRASSHOPPER, 'G'
16
+ new :SPIDER, 'S'
17
+ new :ANT, 'A'
18
+ end
@@ -2,11 +2,10 @@
2
2
  # player color constants
3
3
  require 'typesafe_enum'
4
4
 
5
- # Die Spielerfarben. RED, BLUE oder NONE.
5
+ # Die Spielerfarben. RED oder BLUE
6
6
  class PlayerColor < TypesafeEnum::Base
7
- new :NONE
8
- new :RED
9
- new :BLUE
7
+ new :RED, 'R'
8
+ new :BLUE, 'B'
10
9
 
11
10
  # @param color [PlayerColor]
12
11
  # @return [PlayerColor] Farbe des Gegenspielers
@@ -16,20 +15,11 @@ class PlayerColor < TypesafeEnum::Base
16
15
  PlayerColor::BLUE
17
16
  when PlayerColor::BLUE
18
17
  PlayerColor::RED
19
- when PlayerColor::NONE
20
- PlayerColor::NONE
21
18
  end
22
19
  end
23
20
 
24
- # @param color [PlayerColor] Die Spielerfarbe, zu dem der Feldtyp ermittelt werden soll.
25
- # @return [FieldType] Der zur Spielerfarbe gehörende Feldtyp, also FieldType::RED für PlayerColor::RED und FieldType::BLUE für PlayerColor::BLUE. In allen anderen Fällen nil.
26
- # @see FieldType#player_color
27
- def self.field_type(color)
28
- case color
29
- when PlayerColor::RED
30
- FieldType::RED
31
- when PlayerColor::BLUE
32
- FieldType::BLUE
33
- end
21
+ def opponent
22
+ PlayerColor.opponent_color(self)
34
23
  end
24
+
35
25
  end
@@ -2,7 +2,9 @@
2
2
  # frozen_string_literal: true
3
3
  require 'socket'
4
4
  require_relative 'board'
5
- require_relative 'move'
5
+ require_relative 'set_move'
6
+ require_relative 'drag_move'
7
+ require_relative 'skip_move'
6
8
  require_relative 'player'
7
9
  require_relative 'network'
8
10
  require_relative 'client_interface'
@@ -40,7 +42,12 @@ class Protocol
40
42
  # @param text [String] the xml-string that will be parsed
41
43
  def process_string(text)
42
44
  logger.debug "Parse XML:\n#{text}\n----END XML"
43
- REXML::Document.parse_stream(text, self)
45
+ begin
46
+ REXML::Document.parse_stream(text, self)
47
+ rescue REXML::ParseException => e
48
+ # to parse incomplete xml, ignore missing end tag exceptions
49
+ raise e unless e.message =~ /Missing end tag/
50
+ end
44
51
  end
45
52
 
46
53
 
@@ -113,29 +120,68 @@ class Protocol
113
120
  when 'board'
114
121
  logger.debug 'new board'
115
122
  @gamestate.board = Board.new
116
- @context[:current_tile_index] = nil
117
- @context[:current_tile_direction] = nil
118
123
  when 'field'
119
- type = FieldType.find_by_key(attrs['state'].to_sym)
120
124
  x = attrs['x'].to_i
121
125
  y = attrs['y'].to_i
122
- raise "unexpected field type: #{attrs['type']}. Known types are #{FieldType.map { |t| t.key.to_s }}" if type.nil?
123
- @gamestate.board.add_field(Field.new(x, y, type))
126
+ obstructed = attrs['isObstructed'] == 'true'
127
+ field = Field.new(x, y, [], obstructed)
128
+ @gamestate.board.add_field(field)
129
+ @context[:piece_target] = :field
130
+ @context[:field] = field
131
+ when 'piece'
132
+ owner = PlayerColor.find_by_key(attrs['owner'].to_sym)
133
+ type = PieceType.find_by_key(attrs['type'].to_sym)
134
+ piece = Piece.new(owner, type)
135
+ case @context[:piece_target]
136
+ when :field
137
+ @context[:field].add_piece(piece)
138
+ when :undeployed_red_pieces
139
+ @gamestate.undeployed_red_pieces << piece
140
+ when :undeployed_blue_pieces
141
+ @gamestate.undeployed_blue_pieces << piece
142
+ when :last_move
143
+ @context[:last_move_piece] = piece
144
+ else
145
+ raise "unknown piece target #{@context[:piece_target]}"
146
+ end
147
+ when 'undeployedRedPieces'
148
+ @context[:piece_target] = :undeployed_red_pieces
149
+ @gamestate.undeployed_red_pieces = []
150
+ when 'undeployedBluePieces'
151
+ @context[:piece_target] = :undeployed_blue_pieces
152
+ @gamestate.undeployed_blue_pieces = []
124
153
  when 'lastMove'
125
- direction = Direction.find_by_key(attrs['direction'].to_sym)
126
- x = attrs['x'].to_i
127
- y = attrs['y'].to_i
128
- raise "unexpected direction: #{attrs['direction']}. Known directions are #{Direction.map { |d| d.key.to_s }}" if direction.nil?
129
- @gamestate.last_move = Move.new(x, y, direction)
154
+ type = attrs['class']
155
+ if type == 'skipmove'
156
+ @gamestate.last_move = SkipMove.new
157
+ else
158
+ @context[:last_move_type] = type
159
+ @context[:piece_target] = :last_move
160
+ end
161
+ when 'start'
162
+ @context[:last_move_start] = CubeCoordinates.new(attrs['x'].to_i, attrs['y'].to_i, attrs['z'].to_i)
163
+ when 'destination'
164
+ destination = CubeCoordinates.new(attrs['x'].to_i, attrs['y'].to_i, attrs['z'].to_i)
165
+ case @context[:last_move_type]
166
+ when 'setmove'
167
+ @gamestate.last_move = SetMove.new(@context[:last_move_piece], destination)
168
+ when 'dragmove'
169
+ @gamestate.last_move = SetMove.new(@context[:last_move_start], destination)
170
+ end
130
171
  when 'winner'
131
- winning_player = parsePlayer(attrs)
132
- @gamestate.condition = Condition.new(winning_player, @gamestate.condition.reason)
172
+ # TODO
173
+ #winning_player = parsePlayer(attrs)
174
+ #@gamestate.condition = Condition.new(winning_player, @gamestate.condition.reason)
133
175
  when 'score'
176
+ # TODO
134
177
  # there are two score tags in the result, but reason attribute should be equal on both
135
- @gamestate.condition = Condition.new(@gamestate.condition.winner, attrs['reason'])
178
+ #@gamestate.condition = Condition.new(@gamestate.condition.winner, attrs['reason'])
136
179
  when 'left'
137
180
  logger.debug 'got left event, terminating'
138
181
  @network.disconnect
182
+ when 'sc.protocol.responses.CloseConnection'
183
+ logger.debug 'got left close connection event, terminating'
184
+ @network.disconnect
139
185
  end
140
186
  end
141
187
 
@@ -180,9 +226,31 @@ class Protocol
180
226
  # class interface to supply a method which returns the XML
181
227
  # because XML-generation should be decoupled from internal data
182
228
  # structures.
183
- builder.data(class: 'move', x: move.x, y: move.y, direction: move.direction.key) do |data|
184
- move.hints.each do |hint|
185
- data.hint(content: hint.content)
229
+ case move
230
+ when SetMove
231
+ builder.data(class: 'setmove') do |data|
232
+ data.piece(owner: move.piece.owner.key, type: move.piece.type.key)
233
+ d = move.destination
234
+ data.destination(x: d.x, y: d.y, z: d.z)
235
+ move.hints.each do |hint|
236
+ data.hint(content: hint.content)
237
+ end
238
+ end
239
+ when DragMove
240
+ builder.data(class: 'dragmove') do |data|
241
+ s = move.start
242
+ data.start(x: s.x, y: s.y, z: s.z)
243
+ d = move.destination
244
+ data.destination(x: d.x, y: d.y, z: d.z)
245
+ move.hints.each do |hint|
246
+ data.hint(content: hint.content)
247
+ end
248
+ end
249
+ when SkipMove
250
+ builder.data(class: 'skipmove') do |data|
251
+ move.hints.each do |hint|
252
+ data.hint(content: hint.content)
253
+ end
186
254
  end
187
255
  end
188
256
  builder.target!
@@ -0,0 +1,15 @@
1
+ require_relative 'has_hints'
2
+
3
+ class SetMove
4
+
5
+ include HasHints
6
+
7
+ attr_reader :piece
8
+ attr_reader :destination
9
+
10
+ def initialize(piece, destination)
11
+ @piece = piece
12
+ @destination = destination
13
+ @hints = []
14
+ end
15
+ end
@@ -0,0 +1,8 @@
1
+ require_relative 'has_hints'
2
+ class SkipMove
3
+ include HasHints
4
+
5
+ def initialize
6
+ @hints = []
7
+ end
8
+ end
@@ -4,6 +4,8 @@
4
4
  # Konstanten zum aktuellen Spiel.
5
5
  module Constants
6
6
  ROUND_LIMIT = 30 # Rundenbegrenzung. Nach Ende der angegebenen Runde endet auch das Spiel.
7
- SIZE = 10 # Die Größe des Spielbrettes (Breite und Höhe in Feldern).
8
- GAME_IDENTIFIER = 'swc_2019_piranhas' # Der Identifikator des Spiels. Für die Kommunikation mit dem Spielserver.
7
+ GAME_IDENTIFIER = 'swc_2020_hive' # Der Identifikator des Spiels. Für die Kommunikation mit dem Spielserver.
8
+ STARTING_PIECES = 'QSSSGGBBAAA'
9
+ BOARD_SIZE = 11
10
+ SHIFT = ((BOARD_SIZE - 1) / 2)
9
11
  end
@@ -1,4 +1,4 @@
1
1
  # encoding: UTF-8
2
2
  module SoftwareChallengeClient
3
- VERSION = "19.1.0"
3
+ VERSION = "20.2.0"
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: software_challenge_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 19.1.0
4
+ version: 20.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - 'kwollw '
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2018-12-20 00:00:00.000000000 Z
13
+ date: 2019-11-05 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: typesafe_enum
@@ -249,6 +249,7 @@ files:
249
249
  - ".rspec"
250
250
  - ".rubocop.yml"
251
251
  - ".ruby-version"
252
+ - ".stickler.yml"
252
253
  - AUTHORS
253
254
  - CODE_OF_CONDUCT.md
254
255
  - Dockerfile
@@ -268,23 +269,26 @@ files:
268
269
  - lib/software_challenge_client/board.rb
269
270
  - lib/software_challenge_client/client_interface.rb
270
271
  - lib/software_challenge_client/condition.rb
271
- - lib/software_challenge_client/coordinates.rb
272
+ - lib/software_challenge_client/cube_coordinates.rb
272
273
  - lib/software_challenge_client/debug_hint.rb
273
274
  - lib/software_challenge_client/direction.rb
275
+ - lib/software_challenge_client/drag_move.rb
274
276
  - lib/software_challenge_client/field.rb
275
- - lib/software_challenge_client/field_type.rb
276
277
  - lib/software_challenge_client/game_rule_logic.rb
277
278
  - lib/software_challenge_client/game_state.rb
279
+ - lib/software_challenge_client/has_hints.rb
278
280
  - lib/software_challenge_client/invalid_move_exception.rb
279
- - lib/software_challenge_client/line.rb
280
281
  - lib/software_challenge_client/line_direction.rb
281
282
  - lib/software_challenge_client/logging.rb
282
- - lib/software_challenge_client/move.rb
283
283
  - lib/software_challenge_client/network.rb
284
+ - lib/software_challenge_client/piece.rb
285
+ - lib/software_challenge_client/piece_type.rb
284
286
  - lib/software_challenge_client/player.rb
285
287
  - lib/software_challenge_client/player_color.rb
286
288
  - lib/software_challenge_client/protocol.rb
287
289
  - lib/software_challenge_client/runner.rb
290
+ - lib/software_challenge_client/set_move.rb
291
+ - lib/software_challenge_client/skip_move.rb
288
292
  - lib/software_challenge_client/util/constants.rb
289
293
  - lib/software_challenge_client/version.rb
290
294
  - release.sh
@@ -307,11 +311,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
307
311
  - !ruby/object:Gem::Version
308
312
  version: '0'
309
313
  requirements: []
310
- rubyforge_project:
311
- rubygems_version: 2.6.11
314
+ rubygems_version: 3.0.3
312
315
  signing_key:
313
316
  specification_version: 4
314
317
  summary: This gem provides functions to build a client for the coding competition
315
318
  Software-Challenge 2017.
316
319
  test_files: []
317
- has_rdoc: