software_challenge_client 1.2.1 → 19.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +3 -0
  3. data/README.md +64 -26
  4. data/example/client.rb +1 -71
  5. data/example/main.rb +1 -1
  6. data/lib/software_challenge_client.rb +4 -2
  7. data/lib/software_challenge_client/board.rb +66 -19
  8. data/lib/software_challenge_client/client_interface.rb +10 -5
  9. data/lib/software_challenge_client/condition.rb +2 -2
  10. data/lib/software_challenge_client/coordinates.rb +17 -0
  11. data/lib/software_challenge_client/debug_hint.rb +8 -4
  12. data/lib/software_challenge_client/direction.rb +53 -0
  13. data/lib/software_challenge_client/field.rb +26 -12
  14. data/lib/software_challenge_client/field_type.rb +25 -19
  15. data/lib/software_challenge_client/game_rule_logic.rb +230 -0
  16. data/lib/software_challenge_client/game_state.rb +45 -191
  17. data/lib/software_challenge_client/invalid_move_exception.rb +6 -8
  18. data/lib/software_challenge_client/line.rb +126 -0
  19. data/lib/software_challenge_client/line_direction.rb +15 -0
  20. data/lib/software_challenge_client/logging.rb +3 -2
  21. data/lib/software_challenge_client/move.rb +51 -38
  22. data/lib/software_challenge_client/network.rb +3 -1
  23. data/lib/software_challenge_client/player.rb +0 -39
  24. data/lib/software_challenge_client/player_color.rb +23 -13
  25. data/lib/software_challenge_client/protocol.rb +20 -83
  26. data/lib/software_challenge_client/runner.rb +2 -1
  27. data/lib/software_challenge_client/util/constants.rb +8 -5
  28. data/lib/software_challenge_client/version.rb +1 -1
  29. data/software_challenge_client.gemspec +2 -0
  30. metadata +24 -8
  31. data/lib/software_challenge_client/action.rb +0 -217
  32. data/lib/software_challenge_client/card_type.rb +0 -13
  33. data/lib/software_challenge_client/field_unavailable_exception.rb +0 -17
  34. data/lib/software_challenge_client/game_rules.rb +0 -376
@@ -1,16 +1,14 @@
1
- # encoding: UTF-8
1
+ # encoding: utf-8
2
2
 
3
- # Exception indicating a move which was performed is not valid for the given
4
- # state.
3
+ # Exception, die geworfen wird, wenn ein ungültiger Zug ausgeführt wird.
4
+ # @see Move#perform!
5
5
  class InvalidMoveException < StandardError
6
- def initialize(msg, move_or_action)
7
- # This exception will be thrown by a move or by an individual action,
8
- # depending where the rule violation was detected.
9
- @move_or_action = move_or_action
6
+ def initialize(msg, move)
7
+ @move = move
10
8
  super(msg)
11
9
  end
12
10
 
13
11
  def message
14
- "#{super}: #{@move_or_action}"
12
+ "#{super}: #{@move}"
15
13
  end
16
14
  end
@@ -0,0 +1,126 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require_relative './util/constants'
5
+ require_relative 'direction'
6
+ require_relative 'coordinates'
7
+ require_relative 'line_direction'
8
+
9
+ # Ein Iterator, der alle Koordinatenpaare auf einer Linie des Spielbrettes enthält.
10
+ class Line
11
+ include Enumerable
12
+ include Constants
13
+
14
+ # Erzeugt einen neuen Iterator.
15
+ #
16
+ # @param start [Coordinates]
17
+ # @param direction [LineDirection]
18
+ def initialize(start, direction)
19
+ @direction = direction
20
+ @start = start
21
+ # we will iterate from left to right, so find the leftmost field on the line
22
+ # inside the field note that (0,0) is the lowest, leftmost point, x-axis
23
+ # goes to the right, y-axis goes up
24
+ case @direction
25
+ when LineDirection::HORIZONTAL
26
+ leftmost_x = 0
27
+ leftmost_y = @start.y
28
+ when LineDirection::VERTICAL
29
+ leftmost_x = @start.x
30
+ leftmost_y = SIZE - 1
31
+ when LineDirection::RISING_DIAGONAL
32
+ # for rising diagonals, we have to decrease x and y
33
+ shift = [@start.x, @start.y].min
34
+ leftmost_x = @start.x - shift
35
+ leftmost_y = @start.y - shift
36
+ when LineDirection::FALLING_DIAGONAL
37
+ # for falling diagonals, we have to decrease x and increase y
38
+ shift = [@start.x, (SIZE - 1) - @start.y].min
39
+ leftmost_x = @start.x - shift
40
+ leftmost_y = @start.y + shift
41
+ end
42
+ @xi = leftmost_x
43
+ @yi = leftmost_y
44
+
45
+ @members = []
46
+ while @xi >= 0 && @yi >= 0 && @xi < SIZE && @yi < SIZE
47
+ @members << Coordinates.new(@xi, @yi)
48
+ # horizontal lines and diagonals move right
49
+ if [LineDirection::HORIZONTAL,
50
+ LineDirection::RISING_DIAGONAL,
51
+ LineDirection::FALLING_DIAGONAL].include? @direction
52
+ @xi += 1
53
+ end
54
+ # vertical lines and falling diagonals move down
55
+ if [LineDirection::VERTICAL,
56
+ LineDirection::FALLING_DIAGONAL].include? @direction
57
+ @yi -= 1
58
+ elsif @direction == LineDirection::RISING_DIAGONAL
59
+ # rising diagonals move up
60
+ @yi += 1
61
+ end
62
+ end
63
+ end
64
+
65
+ def each(&block)
66
+ @members.each(&block)
67
+ end
68
+
69
+ # Begrenzt den Iterator auf Koordinatenpaare von Feldern innerhalb der gegebenen beiden Koordinatenpaare (exklusive).
70
+ # Kann als Filter verwendet werden.
71
+ #
72
+ # @example
73
+ # Line.new(
74
+ # Coordinates.new(2, 3), LineDirection::HORIZONTAL
75
+ # ).select do |c|
76
+ # Line.between(Coordinates.new(2, 3), Coordinates.new(5, 3), LineDirection::HORIZONTAL).call(c)
77
+ # end
78
+ def self.between(start, bound, direction)
79
+ lower_x = [start.x, bound.x].min
80
+ lower_y = [start.y, bound.y].min
81
+ higher_x = [start.x, bound.x].max
82
+ higher_y = [start.y, bound.y].max
83
+ proc do |f|
84
+ case direction
85
+ when LineDirection::HORIZONTAL
86
+ f.x > lower_x && f.x < higher_x
87
+ when LineDirection::VERTICAL
88
+ f.y > lower_y && f.y < higher_y
89
+ when LineDirection::RISING_DIAGONAL, LineDirection::FALLING_DIAGONAL
90
+ f.x > lower_x && f.x < higher_x && f.y > lower_y && f.y < higher_y
91
+ else
92
+ throw `unknown direction ${direction}`
93
+ end
94
+ end
95
+ end
96
+
97
+ # @param line_direction [LineDirection] Ausrichtung der Linie
98
+ # @return [Array<Direction>] Die beiden Bewegungsrichtungen, die auf einer Linie mit der Ausrichtung möglich sind.
99
+ def self.directions_for_line_direction(line_direction)
100
+ case line_direction
101
+ when LineDirection::HORIZONTAL
102
+ [Direction::LEFT, Direction::RIGHT]
103
+ when LineDirection::VERTICAL
104
+ [Direction::UP, Direction::DOWN]
105
+ when LineDirection::RISING_DIAGONAL
106
+ [Direction::UP_RIGHT, Direction::DOWN_LEFT]
107
+ when LineDirection::FALLING_DIAGONAL
108
+ [Direction::UP_LEFT, Direction::DOWN_RIGHT]
109
+ end
110
+ end
111
+
112
+ # @param line_direction [Direction] Bewegungsrichtung
113
+ # @return [Array<Direction>] Die Ausrichtung einer Linie, die auf der Bewegungsrichtung liegt.
114
+ def self.line_direction_for_direction(direction)
115
+ case direction
116
+ when Direction::LEFT, Direction::RIGHT
117
+ LineDirection::HORIZONTAL
118
+ when Direction::UP, Direction::DOWN
119
+ LineDirection::VERTICAL
120
+ when Direction::UP_RIGHT, Direction::DOWN_LEFT
121
+ LineDirection::RISING_DIAGONAL
122
+ when Direction::UP_LEFT, Direction::DOWN_RIGHT
123
+ LineDirection::FALLING_DIAGONAL
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ require 'typesafe_enum'
4
+ # Ausrichtung einer Linie auf dem Spielbrett. Mögliche Werte sind:
5
+ # - HORIZONTAL
6
+ # - VERTICAL
7
+ # - RISING_DIAGONAL
8
+ # - FALLING_DIAGONAL
9
+
10
+ class LineDirection < TypesafeEnum::Base
11
+ new :HORIZONTAL
12
+ new :VERTICAL
13
+ new :RISING_DIAGONAL
14
+ new :FALLING_DIAGONAL
15
+ end
@@ -1,9 +1,10 @@
1
+ # coding: utf-8
1
2
  require 'logger'
2
3
 
3
- # This module provides a shared logger to all classes into which it is mixed.
4
+ # Dieses Modul kann inkludiert werden, um eine Logausgabe auf der Konsole verwenden zu können.
4
5
  # See http://stackoverflow.com/a/6768164/390808
5
6
  #
6
- # Usage:
7
+ # Verwendung:
7
8
  #
8
9
  # class MyClass
9
10
  # include Logging
@@ -1,70 +1,83 @@
1
- # encoding: UTF-8
1
+ # encoding: utf-8
2
2
  require_relative 'debug_hint'
3
- require_relative 'action'
4
3
 
5
- # A move that can be performed in Mississippi Queen. A move consists of multiple
6
- # actions in a specific order.
4
+ # Ein Spielzug. Er ist definiert durch das Koordinatenpaar des Ausgangsfeldes (ein Fisch des Spielers, der den Zug machen will) und eine Bewegungsrichtung.
7
5
  class Move
8
- # @!attribute [r] actions
6
+ # @!attribute [r] x
7
+ # @return [Integer] X-Koordinate des Fisches, der bewegt werden soll. Die Spalte ganz links auf dem Spielbrett hat X-Koordinate 0, die ganz rechts 9.
8
+ attr_reader :x
9
+
10
+ # @!attribute [r] y
11
+ # @return [Integer] Y-Koordinate des Fisches, der bewegt werden soll. Die Zeile ganz unten auf dem Spielbrett hat Y-Koordinate 0, die ganz oben 9.
12
+ attr_reader :y
13
+
14
+ # @!attribute [r] direction
9
15
  #
10
- # @return [Array<Action>] List of actions which should be performed in this
11
- # move in the order determined by the array order.
12
- attr_reader :actions
16
+ # @return [Direction] Die Richtung, in die bewegt werden soll.
17
+ attr_reader :direction
13
18
 
14
19
  # @!attribute [r] hints
15
- # @return [Array<DebugHint>] the move's hints
20
+ # @return [Array<DebugHint>] Hinweise, die an den Zug angeheftet werden sollen. Siehe {DebugHint}.
16
21
  attr_reader :hints
17
22
 
18
- # Initializer
19
- #
20
- def initialize(actions = [], hints = [])
21
- @actions = actions
22
- @hints = hints
23
+ # Erstellt einen neuen Zug.
24
+ # @param x [Integer]
25
+ # @param y [Integer]
26
+ # @param direction [Direction]
27
+ def initialize(x, y, direction)
28
+ @x = x
29
+ @y = y
30
+ @direction = direction
31
+ @hints = []
23
32
  end
24
33
 
25
- # adds a hint to the move
26
- # @param hint [DebugHint] the added hint
34
+ # @param hint [DebugHint]
27
35
  def add_hint(hint)
28
36
  @hints.push(hint)
29
37
  end
30
38
 
31
39
  def ==(other)
32
- actions.size == other.actions.size &&
33
- actions.zip(other.actions).map { |a, b| a == b }.all?
40
+ x == other.x && y == other.y && direction == other.direction
34
41
  end
35
42
 
36
43
  def to_s
37
- "Move: #{actions}"
44
+ "Move: (#{x},#{y}) #{direction}"
38
45
  end
39
46
 
40
- def add_action(action)
41
- @actions << action
47
+ # @return [Coordinates] Die Koordinaten des Ausgangsfeldes des Zuges als Koordinatenpaar.
48
+ def from_field
49
+ Coordinates.new(x, y)
42
50
  end
43
51
 
44
- def add_action_with_order(action, index)
45
- @actions[index] = action
52
+ # Überprüft, ob der Zug in dem gegebenen Spielzustand regelkonform ausgeführt werden kann.
53
+ # @param gamestate [GameState]
54
+ # @return [Boolean]
55
+ def valid?(gamestate)
56
+ GameRuleLogic.valid_move(self, gamestate.board)
46
57
  end
47
58
 
59
+ # Führt den Zug in dem gegebenen Spielzustand aus. Sollte dabei gegen Spielregeln verstossen werden, wird eine InvalidMoveException geworfen.
60
+ # @param gamestate [GameState]
48
61
  def perform!(gamestate)
49
- raise InvalidMoveException.new(
50
- "Zug enthält keine Aktionen (zum Aussetzen die Aktion Skip benutzen).",
51
- self) if @actions.empty?
52
- @actions.each do |action|
53
- action.perform!(gamestate)
62
+ if GameRuleLogic.valid_move(self, gamestate.board)
63
+ type = gamestate.board.field(x, y).type
64
+ gamestate.board.change_field(x, y, FieldType::EMPTY)
65
+ target = GameRuleLogic.move_target(self, gamestate.board)
66
+ gamestate.board.change_field(target.x, target.y, type)
67
+ else
68
+ raise InvalidMoveException.new('Invalid move', self)
54
69
  end
55
- raise InvalidMoveException.new(
56
- 'Es muss eine Karte gespielt werden.',
57
- self) if gamestate.current_player.must_play_card
58
70
  # change the state to the next turn
59
71
  gamestate.last_move = self
60
72
  gamestate.turn += 1
61
73
  gamestate.switch_current_player
62
- # change carrots for next player if on first/second-position-field
63
- if gamestate.current_field.type == FieldType::POSITION_1 && gamestate.is_first(gamestate.current_player)
64
- gamestate.current_player.carrots += 10
65
- end
66
- if gamestate.current_field.type == FieldType::POSITION_2 && gamestate.is_second(gamestate.current_player)
67
- gamestate.current_player.carrots += 30
68
- end
74
+ end
75
+
76
+
77
+ # Ermittelt die Koordinaten des Zielfeldes des Zuges mit einer gegebenen Zugweite.
78
+ # @param speed [Integer] Die Zugweite. Entspricht normalerweise der Anzahl der Fische auf der Bewegungslinie.
79
+ # @return [Coordinates] Koordinaten des Zielfeldes. Eventuell ausserhalb des Spielbrettes.
80
+ def target_field(speed)
81
+ direction.translate(from_field, speed)
69
82
  end
70
83
  end
@@ -7,10 +7,12 @@ require 'rexml/element'
7
7
  require_relative 'protocol'
8
8
  require_relative 'board'
9
9
  require_relative 'client_interface'
10
+ require_relative 'util/constants'
10
11
 
11
12
  # This class handles the socket connection to the server
12
13
  class Network
13
14
  include Logging
15
+ include Constants
14
16
 
15
17
  # @!attribute [r] connected
16
18
  # @return [Boolean] true, if the client is connected to a server
@@ -43,7 +45,7 @@ class Network
43
45
  element.add_attribute('reservationCode', @reservation_id)
44
46
  else
45
47
  element = REXML::Element.new('join')
46
- element.add_attribute('gameType', 'swc_2018_hase_und_igel')
48
+ element.add_attribute('gameType', GAME_IDENTIFIER)
47
49
  end
48
50
  document.add(element)
49
51
  sendXML(document)
@@ -1,5 +1,4 @@
1
1
  # encoding: UTF-8
2
- require_relative 'card_type'
3
2
 
4
3
  # Ein Spieler
5
4
  class Player
@@ -11,53 +10,15 @@ class Player
11
10
  # @return [PlayerColor] die Farbe des Spielers, Rot oder Blau
12
11
  attr_reader :color
13
12
 
14
- # @!attribute [rw] points
15
- # @return [Integer] der aktuelle Punktestand des Spielers
16
- attr_accessor :points
17
-
18
- # @!attribute [rw] index
19
- # @return [Integer] die aktuelle Position des Spielers auf dem Spielbrett,
20
- # entspricht index des Feldes, von 0 bis 64
21
- attr_accessor :index
22
-
23
- # @!attribute [rw] carrots
24
- # @return [Integer] die aktuelle Anzahl Karotten des Spielers
25
- attr_accessor :carrots
26
-
27
- # @!attribute [rw] salads
28
- # @return [Integer] die aktuelle Anzahl Salate des Spielers
29
- attr_accessor :salads
30
-
31
- # @!attribute [rw] cards
32
- # @return [Array[CardType]] die noch nicht gespielten Karten
33
- attr_accessor :cards
34
-
35
- # @!attribute [rw] last_non_skip_action
36
- # @return [Action] letzte Aktion, die kein Skip war
37
- attr_accessor :last_non_skip_action
38
-
39
- # @!attribute [rw] must_play_card
40
- # @return [Boolean] zeigt an, ob eine Karte gespielt werden muss, wird in Zugvalidierung verwendet.
41
- attr_accessor :must_play_card
42
-
43
13
  # Konstruktor
44
14
  # @param color [PlayerColor] Farbe
45
15
  # @param name [String] Name
46
16
  def initialize(color, name)
47
17
  @color = color
48
18
  @name = name
49
- @points = 0
50
- @index = 0
51
- @carrots = 68
52
- @salads = 5
53
- @cards = CardType.to_a
54
19
  end
55
20
 
56
21
  def ==(other)
57
22
  color == other.color
58
23
  end
59
-
60
- def owns_card_of_type(card_type)
61
- cards.include? card_type
62
- end
63
24
  end
@@ -1,25 +1,35 @@
1
- # encoding: UTF-8
1
+ # encoding: utf-8
2
2
  # player color constants
3
3
  require 'typesafe_enum'
4
- class PlayerColor < TypesafeEnum::Base
5
4
 
5
+ # Die Spielerfarben. RED, BLUE oder NONE.
6
+ class PlayerColor < TypesafeEnum::Base
6
7
  new :NONE
7
8
  new :RED
8
9
  new :BLUE
9
10
 
10
- # Returns the opponents Color
11
- #
12
- # @param color [PlayerColor] The player's color, whose opponent needs to be found
13
- # @return [PlayerColor] the opponent's color
11
+ # @param color [PlayerColor]
12
+ # @return [PlayerColor] Farbe des Gegenspielers
14
13
  def self.opponent_color(color)
15
- if color == PlayerColor::RED
16
- return PlayerColor::BLUE
17
- end
18
- if color == PlayerColor::BLUE
19
- return PlayerColor::RED
14
+ case color
15
+ when PlayerColor::RED
16
+ PlayerColor::BLUE
17
+ when PlayerColor::BLUE
18
+ PlayerColor::RED
19
+ when PlayerColor::NONE
20
+ PlayerColor::NONE
20
21
  end
21
- if color == PlayerColor::NONE
22
- return PlayerColor::NONE
22
+ end
23
+
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
23
33
  end
24
34
  end
25
35
  end
@@ -56,8 +56,6 @@ class Protocol
56
56
  case name
57
57
  when 'board'
58
58
  logger.debug @gamestate.board.to_s
59
- when 'type'
60
- @context[:player].cards << CardType.find_by_key(@context[:last_text].to_sym)
61
59
  end
62
60
  end
63
61
 
@@ -92,8 +90,8 @@ class Protocol
92
90
  logger.debug 'new gamestate'
93
91
  @gamestate = GameState.new
94
92
  @gamestate.turn = attrs['turn'].to_i
95
- @gamestate.start_player_color = attrs['startPlayer'] == 'RED' ? PlayerColor::RED : PlayerColor::BLUE
96
- @gamestate.current_player_color = attrs['currentPlayer'] == 'RED' ? PlayerColor::RED : PlayerColor::BLUE
93
+ @gamestate.start_player_color = attrs['startPlayerColor'] == 'RED' ? PlayerColor::RED : PlayerColor::BLUE
94
+ @gamestate.current_player_color = attrs['currentPlayerColor'] == 'RED' ? PlayerColor::RED : PlayerColor::BLUE
97
95
  logger.debug "Turn: #{@gamestate.turn}"
98
96
  when 'red'
99
97
  logger.debug 'new red player'
@@ -116,38 +114,18 @@ class Protocol
116
114
  @gamestate.board = Board.new
117
115
  @context[:current_tile_index] = nil
118
116
  @context[:current_tile_direction] = nil
119
- when 'fields'
120
- type = FieldType.find_by_key(attrs['type'].to_sym)
121
- index = attrs['index'].to_i
117
+ when 'field'
118
+ type = FieldType.find_by_key(attrs['state'].to_sym)
119
+ x = attrs['x'].to_i
120
+ y = attrs['y'].to_i
122
121
  raise "unexpected field type: #{attrs['type']}. Known types are #{FieldType.map { |t| t.key.to_s }}" if type.nil?
123
- @gamestate.board.fields[index] = Field.new(type, index)
122
+ @gamestate.board.add_field(Field.new(x, y, type))
124
123
  when 'lastMove'
125
- @gamestate.last_move = Move.new
126
- when 'advance'
127
- @gamestate.last_move.add_action_with_order(
128
- Advance.new(attrs['distance'].to_i), attrs['order'].to_i
129
- )
130
- when 'card'
131
- @gamestate.last_move.add_action_with_order(
132
- Card.new(CardType.find_by_key(attrs['type'].to_sym), attrs['value'].to_i),
133
- attrs['order'].to_i
134
- )
135
- when 'skip'
136
- @gamestate.last_move.add_action_with_order(
137
- Skip.new, attrs['order'].to_i
138
- )
139
- when 'eatSalad'
140
- @gamestate.last_move.add_action_with_order(
141
- EatSalad.new, attrs['order'].to_i
142
- )
143
- when 'fallBack'
144
- @gamestate.last_move.add_action_with_order(
145
- FallBack.new, attrs['order'].to_i
146
- )
147
- when 'exchangeCarrots'
148
- @gamestate.last_move.add_action_with_order(
149
- ExchangeCarrots.new(attrs['value'].to_i), attrs['order'].to_i
150
- )
124
+ direction = Direction.find_by_key(attrs['direction'].to_sym)
125
+ x = attrs['x'].to_i
126
+ y = attrs['y'].to_i
127
+ raise "unexpected direction: #{attrs['direction']}. Known directions are #{Direction.map { |d| d.key.to_s }}" if direction.nil?
128
+ @gamestate.last_move = Move.new(x, y, direction)
151
129
  when 'winner'
152
130
  winning_player = parsePlayer(attrs)
153
131
  @gamestate.condition = Condition.new(winning_player)
@@ -155,24 +133,6 @@ class Protocol
155
133
  when 'left'
156
134
  logger.debug 'got left event, terminating'
157
135
  @network.disconnect
158
- when 'lastNonSkipAction'
159
- @context[:player].last_non_skip_action =
160
- case attrs['class']
161
- when 'advance'
162
- Advance.new(attrs['distance'].to_i)
163
- when 'card'
164
- Card.new(CardType.find_by_key(attrs['type'].to_sym), attrs['value'].to_i)
165
- when 'skip'
166
- Skip.new
167
- when 'eatSalad'
168
- EatSalad.new
169
- when 'fallBack'
170
- FallBack.new
171
- when 'exchangeCarrots'
172
- ExchangeCarrots.new(attrs['value'].to_i)
173
- else
174
- raise "Unknown action type #{attrs['class']}"
175
- end
176
136
  end
177
137
  end
178
138
 
@@ -181,16 +141,10 @@ class Protocol
181
141
  # @param attributes [Hash] Attributes for the new Player.
182
142
  # @return [Player] The created Player object.
183
143
  def parsePlayer(attributes)
184
- player = Player.new(
144
+ Player.new(
185
145
  PlayerColor.find_by_key(attributes['color'].to_sym),
186
146
  attributes['displayName']
187
147
  )
188
- player.points = attributes['points'].to_i
189
- player.index = attributes['index'].to_i
190
- player.carrots = attributes['carrots'].to_i
191
- player.salads = attributes['salads'].to_i
192
- player.cards = []
193
- player
194
148
  end
195
149
 
196
150
  # send a xml document
@@ -219,32 +173,15 @@ class Protocol
219
173
  # @param move [Move] The move to convert to XML.
220
174
  def move_to_xml(move)
221
175
  builder = Builder::XmlMarkup.new(indent: 2)
222
- builder.data(class: 'move') do |data|
223
- move.actions.each_with_index do |action, index|
224
- # Converting every action type here instead of requiring the Action
225
- # class interface to supply a method which returns the action hash
226
- # because XML-generation should be decoupled from internal data
227
- # structures.
228
- attribute = case action.type
229
- when :advance
230
- { distance: action.distance }
231
- when :skip, :eat_salad, :fall_back
232
- {}
233
- when :card
234
- { type: action.card_type.key.to_s, value: action.value }
235
- when :exchange_carrots
236
- { value: action.value }
237
- else
238
- raise "unknown action type: #{action.type.inspect}. "\
239
- "Can't convert to XML!"
240
- end
241
- attribute[:order] = index
242
- data.tag!(snake_case_to_lower_camel_case(action.type.to_s), attribute)
176
+ # Converting every the move here instead of requiring the Move
177
+ # class interface to supply a method which returns the XML
178
+ # because XML-generation should be decoupled from internal data
179
+ # structures.
180
+ builder.data(class: 'move', x: move.x, y: move.y, direction: move.direction.key) do |data|
181
+ move.hints.each do |hint|
182
+ data.hint(content: hint.content)
243
183
  end
244
184
  end
245
- move.hints.each do |hint|
246
- data.hint(content: hint.content)
247
- end
248
185
  builder.target!
249
186
  end
250
187