software_challenge_client 19.1.0 → 20.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: