software_challenge_client 0.1.5 → 0.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/.rubocop.yml +6 -0
- data/AUTHORS +6 -0
- data/Guardfile +44 -0
- data/README.md +45 -0
- data/RELEASES.md +4 -0
- data/develop.sh +3 -0
- data/example/client.rb +45 -17
- data/example/main.rb +1 -1
- data/generate-authors.sh +19 -0
- data/lib/software_challenge_client.rb +18 -15
- data/lib/software_challenge_client/action.rb +278 -0
- data/lib/software_challenge_client/board.rb +74 -289
- data/lib/software_challenge_client/client_interface.rb +8 -3
- data/lib/software_challenge_client/condition.rb +2 -4
- data/lib/software_challenge_client/debug_hint.rb +3 -25
- data/lib/software_challenge_client/direction.rb +39 -0
- data/lib/software_challenge_client/field.rb +34 -12
- data/lib/software_challenge_client/field_type.rb +29 -8
- data/lib/software_challenge_client/field_unavailable_exception.rb +17 -0
- data/lib/software_challenge_client/game_state.rb +88 -255
- data/lib/software_challenge_client/invalid_move_exception.rb +16 -0
- data/lib/software_challenge_client/logging.rb +24 -0
- data/lib/software_challenge_client/move.rb +36 -35
- data/lib/software_challenge_client/network.rb +16 -19
- data/lib/software_challenge_client/player.rb +47 -10
- data/lib/software_challenge_client/player_color.rb +8 -7
- data/lib/software_challenge_client/protocol.rb +131 -83
- data/lib/software_challenge_client/runner.rb +9 -7
- data/lib/software_challenge_client/version.rb +1 -1
- data/release.sh +9 -0
- data/software_challenge_client.gemspec +20 -8
- metadata +175 -7
- data/lib/software_challenge_client/connection.rb +0 -56
@@ -0,0 +1,16 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Exception indicating a move which was performed is not valid for the given
|
4
|
+
# state.
|
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
|
10
|
+
super(msg)
|
11
|
+
end
|
12
|
+
|
13
|
+
def message
|
14
|
+
"#{super}: #{@move_or_action}"
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
# This module provides a shared logger to all classes into which it is mixed.
|
4
|
+
# See http://stackoverflow.com/a/6768164/390808
|
5
|
+
#
|
6
|
+
# Usage:
|
7
|
+
#
|
8
|
+
# class MyClass
|
9
|
+
# include Logging
|
10
|
+
#
|
11
|
+
# def a_method(x)
|
12
|
+
# logger.debug "you provided #{x}"
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
module Logging
|
16
|
+
def logger
|
17
|
+
Logging.logger
|
18
|
+
end
|
19
|
+
|
20
|
+
# Global, memoized, lazy initialized instance of a logger
|
21
|
+
def self.logger
|
22
|
+
@logger ||= Logger.new(STDOUT)
|
23
|
+
end
|
24
|
+
end
|
@@ -1,59 +1,60 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
require_relative 'debug_hint'
|
3
|
+
require_relative 'action'
|
3
4
|
|
4
|
-
#
|
5
|
-
#
|
5
|
+
# A move that can be performed in Mississippi Queen. A move consists of multiple
|
6
|
+
# actions in a specific order.
|
6
7
|
class Move
|
7
|
-
# @!attribute [r]
|
8
|
-
#
|
9
|
-
|
10
|
-
#
|
11
|
-
|
12
|
-
|
8
|
+
# @!attribute [r] actions
|
9
|
+
#
|
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
|
13
|
+
|
13
14
|
# @!attribute [r] hints
|
14
15
|
# @return [Array<DebugHint>] the move's hints
|
15
16
|
attr_reader :hints
|
16
17
|
|
17
18
|
# Initializer
|
18
19
|
#
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
@x = x
|
23
|
-
@y = y
|
24
|
-
@hints = Array.new
|
20
|
+
def initialize
|
21
|
+
@actions = []
|
22
|
+
@hints = []
|
25
23
|
end
|
26
24
|
|
27
|
-
# @overload addHint(hint)
|
28
25
|
# adds a hint to the move
|
29
26
|
# @param hint [DebugHint] the added hint
|
30
|
-
|
31
|
-
|
32
|
-
# @param key the added hint's key
|
33
|
-
# @param value the added hint's value
|
34
|
-
# @overload addHint(string)
|
35
|
-
# adds a hint to the move
|
36
|
-
# @param hint [String] the added hint's content
|
37
|
-
def addHint(hint)
|
38
|
-
@hints.push(hint);
|
27
|
+
def add_hint(hint)
|
28
|
+
@hints.push(hint)
|
39
29
|
end
|
40
30
|
|
41
|
-
|
42
|
-
|
43
|
-
|
31
|
+
def ==(other)
|
32
|
+
actions.size == other.actions.size &&
|
33
|
+
actions.zip(other.actions).map { |a, b| a == b }.all?
|
44
34
|
end
|
45
35
|
|
46
|
-
|
47
|
-
|
48
|
-
self.addHint(DebugHint.new(string))
|
36
|
+
def to_s
|
37
|
+
"Move: #{actions}"
|
49
38
|
end
|
50
39
|
|
51
|
-
def
|
52
|
-
|
40
|
+
def add_action(action)
|
41
|
+
@actions << action
|
53
42
|
end
|
54
43
|
|
55
|
-
def
|
56
|
-
|
44
|
+
def add_action_with_order(action, index)
|
45
|
+
@actions[index] = action
|
57
46
|
end
|
58
47
|
|
59
|
-
|
48
|
+
def perform!(gamestate, current_player)
|
49
|
+
# check if acceleration is only first action
|
50
|
+
other_action_before = false
|
51
|
+
@actions.each do |a|
|
52
|
+
if a.type != :acceleration
|
53
|
+
other_action_before = true
|
54
|
+
elsif other_action_before
|
55
|
+
raise InvalidMoveException.new('Beschleunigung muss am Anfang des Zuges geschehen.', self)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
@actions.each { |a| a.perform!(gamestate, current_player) }
|
59
|
+
end
|
60
|
+
end
|
@@ -1,14 +1,17 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
require 'socket'
|
3
|
+
require 'rexml/document'
|
4
|
+
require 'rexml/element'
|
5
|
+
|
3
6
|
require_relative 'protocol'
|
4
7
|
require_relative 'board'
|
5
8
|
require_relative 'client_interface'
|
6
|
-
require 'rexml/document'
|
7
|
-
require 'rexml/element'
|
8
9
|
|
9
|
-
# @author Ralf-Tobias Diekert
|
10
10
|
# This class handles the socket connection to the server
|
11
11
|
class Network
|
12
|
+
|
13
|
+
include Logging
|
14
|
+
|
12
15
|
@socket
|
13
16
|
@host
|
14
17
|
@port
|
@@ -31,8 +34,6 @@ class Network
|
|
31
34
|
@protocol = Protocol.new(self, @client)
|
32
35
|
@reservationID = reservation || ''
|
33
36
|
@receiveBuffer = ''
|
34
|
-
|
35
|
-
puts '> Network/Socket created.'
|
36
37
|
end
|
37
38
|
|
38
39
|
# connects the client with a given server
|
@@ -40,6 +41,7 @@ class Network
|
|
40
41
|
# @return [Boolean] true, if successfully connected to the server
|
41
42
|
def connect
|
42
43
|
@socket = TCPSocket.open(@host, @port)
|
44
|
+
logger.info 'Connection to server established.'
|
43
45
|
@connected = true
|
44
46
|
|
45
47
|
self.sendString('<protocol>')
|
@@ -52,7 +54,7 @@ class Network
|
|
52
54
|
else
|
53
55
|
document = REXML::Document.new
|
54
56
|
element = REXML::Element.new('join')
|
55
|
-
element.add_attribute('gameType', '
|
57
|
+
element.add_attribute('gameType', 'swc_2017_mississippi_queen')
|
56
58
|
document.add(element)
|
57
59
|
self.sendXML(document)
|
58
60
|
end
|
@@ -67,12 +69,12 @@ class Network
|
|
67
69
|
@connected = false
|
68
70
|
@socket.close
|
69
71
|
end
|
70
|
-
|
72
|
+
logger.info 'Connection to server closed.'
|
71
73
|
end
|
72
74
|
|
73
75
|
# reads from the socket until "</room>" is read
|
74
76
|
def readString
|
75
|
-
|
77
|
+
logger.debug 'reading'
|
76
78
|
sockMsg = ''
|
77
79
|
if(!@connected)
|
78
80
|
return
|
@@ -80,8 +82,7 @@ class Network
|
|
80
82
|
|
81
83
|
line =''
|
82
84
|
char = ''
|
83
|
-
while line!="</room>"
|
84
|
-
char = @socket.getc
|
85
|
+
while line != "</room>" && !(char = @socket.getc).nil?
|
85
86
|
line+=char
|
86
87
|
if char=='\n' || char==' '
|
87
88
|
|
@@ -89,7 +90,7 @@ class Network
|
|
89
90
|
end
|
90
91
|
sockMsg += char
|
91
92
|
end
|
92
|
-
|
93
|
+
logger.debug 'ended reading'
|
93
94
|
if sockMsg != ''
|
94
95
|
|
95
96
|
@receiveBuffer.concat(sockMsg)
|
@@ -97,12 +98,10 @@ class Network
|
|
97
98
|
# Remove <protocol> tag
|
98
99
|
@receiveBuffer = @receiveBuffer.gsub('<protocol>', '')
|
99
100
|
|
100
|
-
|
101
|
-
puts ''
|
102
|
-
#puts @receiveBuffer
|
101
|
+
logger.debug "Received XML from server: #{@receiveBuffer}"
|
103
102
|
|
104
103
|
# Process text
|
105
|
-
@protocol.
|
104
|
+
@protocol.process_string("<msg>#{@receiveBuffer}</msg>");
|
106
105
|
self.emptyReceiveBuffer
|
107
106
|
end
|
108
107
|
return true
|
@@ -129,15 +128,13 @@ class Network
|
|
129
128
|
def sendString(s)
|
130
129
|
if(@connected)
|
131
130
|
@socket.print(s);
|
132
|
-
|
133
|
-
puts ''
|
134
|
-
puts(s);
|
131
|
+
logger.debug "Sending: #{s}"
|
135
132
|
end
|
136
133
|
end
|
137
134
|
|
138
135
|
# sends a xml Document to the buffer
|
139
136
|
#
|
140
|
-
# @param xml [REXML::
|
137
|
+
# @param xml [REXML::Document] the Document, that will be sent
|
141
138
|
def sendXML(xml)
|
142
139
|
text = ''
|
143
140
|
xml.write(text)
|
@@ -1,25 +1,62 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
require_relative 'player_color'
|
3
2
|
|
4
|
-
#
|
5
|
-
# A player, participating at a game
|
3
|
+
# A player, participating in a game
|
6
4
|
class Player
|
5
|
+
# @!attribute [r] name
|
6
|
+
# @return [PlayerColor] the player's name
|
7
|
+
attr_reader :name
|
8
|
+
|
7
9
|
# @!attribute [r] color
|
8
10
|
# @return [PlayerColor] the player's color
|
9
11
|
attr_reader :color
|
12
|
+
|
10
13
|
# @!attribute [rw] points
|
11
14
|
# @return [Integer] the player's points
|
12
15
|
attr_accessor :points
|
13
16
|
|
17
|
+
# @!attribute [rw] velocity
|
18
|
+
# @return [Integer] the player's current velocity
|
19
|
+
attr_accessor :velocity
|
20
|
+
|
21
|
+
# @!attribute [rw] coal
|
22
|
+
# @return [Integer] the player's current coal supply
|
23
|
+
attr_accessor :coal
|
24
|
+
|
25
|
+
# @!attribute [rw] direction
|
26
|
+
# @return [Direction] the player's current direction
|
27
|
+
attr_accessor :direction
|
28
|
+
|
29
|
+
# @!attribute [rw] x
|
30
|
+
# @return [Direction] the player's current x-position
|
31
|
+
attr_accessor :x
|
32
|
+
|
33
|
+
# @!attribute [rw] y
|
34
|
+
# @return [Direction] the player's current y-position
|
35
|
+
attr_accessor :y
|
36
|
+
|
37
|
+
# @!attribute [rw] movement
|
38
|
+
# @return [Direction] the player's current movement points
|
39
|
+
attr_accessor :movement
|
40
|
+
|
41
|
+
# @!attribute [rw] passengers
|
42
|
+
# @return [Integer] how many passengers the player's has picked up
|
43
|
+
attr_accessor :passengers
|
44
|
+
|
14
45
|
# Initializer
|
15
|
-
# @param the new player's color
|
16
|
-
|
46
|
+
# @param color [PlayerColor] the new player's color
|
47
|
+
# @param name [String] the new player's name (for displaying)
|
48
|
+
def initialize(color, name)
|
17
49
|
@color = color
|
18
|
-
|
50
|
+
@name = name
|
51
|
+
@points = 0
|
52
|
+
@velocity = 1
|
53
|
+
@movement = 1
|
54
|
+
@coal = 6
|
55
|
+
@passengers = 0
|
56
|
+
@direction = Direction::RIGHT
|
19
57
|
end
|
20
58
|
|
21
|
-
def ==(
|
22
|
-
|
59
|
+
def ==(other)
|
60
|
+
color == other.color
|
23
61
|
end
|
24
|
-
|
25
|
-
end
|
62
|
+
end
|
@@ -1,10 +1,11 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
#
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
2
|
+
# player color constants
|
3
|
+
require 'typesafe_enum'
|
4
|
+
class PlayerColor < TypesafeEnum::Base
|
5
|
+
|
6
|
+
new :NONE
|
7
|
+
new :RED
|
8
|
+
new :BLUE
|
8
9
|
|
9
10
|
# Returns the opponents Color
|
10
11
|
#
|
@@ -21,4 +22,4 @@ module PlayerColor
|
|
21
22
|
return PlayerColor::NONE
|
22
23
|
end
|
23
24
|
end
|
24
|
-
end
|
25
|
+
end
|
@@ -1,43 +1,46 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
2
3
|
require 'socket'
|
3
4
|
require_relative 'board'
|
4
5
|
require_relative 'move'
|
5
6
|
require_relative 'player'
|
6
7
|
require_relative 'network'
|
7
|
-
require_relative 'connection'
|
8
8
|
require_relative 'client_interface'
|
9
9
|
require 'rexml/document'
|
10
10
|
require 'rexml/streamlistener'
|
11
|
+
require 'builder'
|
11
12
|
|
12
|
-
#
|
13
|
-
#
|
13
|
+
# This class handles communication to the server over the XML communication
|
14
|
+
# protocol. Messages from the server are parsed and moves are serialized and
|
15
|
+
# send back.
|
14
16
|
class Protocol
|
17
|
+
include Logging
|
15
18
|
include REXML::StreamListener
|
16
19
|
|
17
20
|
# @!attribute [r] gamestate
|
18
21
|
# @return [Gamestate] current gamestate
|
19
22
|
attr_reader :gamestate
|
20
|
-
# @!attribute [rw]
|
23
|
+
# @!attribute [rw] roomId
|
21
24
|
# @return [String] current room id
|
22
|
-
attr_accessor :
|
25
|
+
attr_accessor :roomId
|
23
26
|
# @!attribute [r] client
|
24
27
|
# @return [ClientInterface] current client
|
25
28
|
attr_reader :client
|
26
|
-
@network
|
27
29
|
|
28
30
|
def initialize(network, client)
|
29
31
|
@gamestate = GameState.new
|
30
|
-
@network
|
31
|
-
|
32
|
+
@network = network
|
33
|
+
@client = client
|
34
|
+
@context = {} # for saving context when stream-parsing the XML
|
35
|
+
@client.gamestate = @gamestate
|
32
36
|
end
|
33
37
|
|
34
38
|
# starts xml-string parsing
|
35
39
|
#
|
36
40
|
# @param text [String] the xml-string that will be parsed
|
37
|
-
def
|
38
|
-
|
39
|
-
|
40
|
-
REXML::Document.parse_stream(text, list)
|
41
|
+
def process_string(text)
|
42
|
+
logger.debug "Parse XML:\n#{text}\n----END XML"
|
43
|
+
REXML::Document.parse_stream(text, self)
|
41
44
|
end
|
42
45
|
|
43
46
|
# called if an end-tag is read
|
@@ -45,10 +48,10 @@ class Protocol
|
|
45
48
|
# @param name [String] the end-tag name, that was read
|
46
49
|
def tag_end(name)
|
47
50
|
case name
|
48
|
-
when
|
49
|
-
|
50
|
-
when
|
51
|
-
|
51
|
+
when 'board'
|
52
|
+
logger.debug @gamestate.board.to_s
|
53
|
+
when 'condition'
|
54
|
+
logger.info 'Game ended'
|
52
55
|
@network.disconnect
|
53
56
|
end
|
54
57
|
end
|
@@ -61,85 +64,91 @@ class Protocol
|
|
61
64
|
# @param attrs [Dictionary<String, String>] Attributes attached to the tag
|
62
65
|
def tag_start(name, attrs)
|
63
66
|
case name
|
64
|
-
when
|
65
|
-
@
|
66
|
-
|
67
|
-
when
|
68
|
-
|
69
|
-
if attrs['class'] ==
|
70
|
-
@client.gamestate =
|
67
|
+
when 'room'
|
68
|
+
@roomId = attrs['roomId']
|
69
|
+
logger.info 'roomId : ' + @roomId
|
70
|
+
when 'data'
|
71
|
+
logger.debug "data(class) : #{attrs['class']}"
|
72
|
+
if attrs['class'] == 'sc.framework.plugins.protocol.MoveRequest'
|
73
|
+
@client.gamestate = gamestate
|
71
74
|
move = @client.getMove
|
72
|
-
|
73
|
-
document.add_element('room',{'roomId' => @roomID})
|
74
|
-
data = REXML::Element.new('data')
|
75
|
-
data.add_attribute('class', 'move')
|
76
|
-
data.add_attribute('x', move.x)
|
77
|
-
data.add_attribute('y', move.y)
|
78
|
-
document.root.add_element(data)
|
79
|
-
for h in move.hints
|
80
|
-
hint = REXML::Element.new('hint')
|
81
|
-
hint.add_attribute('content', h.content)
|
82
|
-
document.root.elements['data'].elements << hint
|
83
|
-
end
|
84
|
-
self.sendXml(document)
|
75
|
+
sendString(move_to_xml(move))
|
85
76
|
end
|
86
|
-
if attrs['class'] ==
|
87
|
-
|
88
|
-
puts attrs['message']
|
77
|
+
if attrs['class'] == 'error'
|
78
|
+
logger.info "Game ended - ERROR: #{attrs['message']}"
|
89
79
|
@network.disconnect
|
90
80
|
end
|
91
|
-
when
|
92
|
-
|
81
|
+
when 'state'
|
82
|
+
logger.debug 'new gamestate'
|
83
|
+
@gamestate = GameState.new
|
93
84
|
@gamestate.turn = attrs['turn'].to_i
|
94
|
-
@gamestate.
|
95
|
-
@gamestate.
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
@gamestate.
|
101
|
-
when
|
102
|
-
|
103
|
-
@gamestate.
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
@
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
type = FieldType::RED
|
116
|
-
when 'BLUE'
|
117
|
-
type = FieldType::BLUE
|
118
|
-
when "winner"
|
119
|
-
puts "Game ended"
|
120
|
-
@network.disconnect
|
121
|
-
end
|
122
|
-
|
123
|
-
case attrs['owner']
|
124
|
-
when 'RED'
|
125
|
-
ownerColor = PlayerColor::RED
|
126
|
-
when 'BLUE'
|
127
|
-
ownerColor = PlayerColor::BLUE
|
128
|
-
end
|
85
|
+
@gamestate.start_player_color = attrs['startPlayer'] == 'RED' ? PlayerColor::RED : PlayerColor::BLUE
|
86
|
+
@gamestate.current_player_color = attrs['currentPlayer'] == 'RED' ? PlayerColor::RED : PlayerColor::BLUE
|
87
|
+
@gamestate.additional_free_turn_after_push = attrs['freeTurn'] == 'true'
|
88
|
+
logger.debug "Turn: #{@gamestate.turn}"
|
89
|
+
when 'red'
|
90
|
+
logger.debug 'new red player'
|
91
|
+
@gamestate.add_player(parsePlayer(PlayerColor::RED, attrs))
|
92
|
+
when 'blue'
|
93
|
+
logger.debug 'new blue player'
|
94
|
+
@gamestate.add_player(parsePlayer(PlayerColor::BLUE, attrs))
|
95
|
+
when 'board'
|
96
|
+
logger.debug 'new board'
|
97
|
+
@gamestate.board = Board.new
|
98
|
+
@context[:current_tile_index] = nil
|
99
|
+
@context[:current_tile_direction] = nil
|
100
|
+
when 'tile'
|
101
|
+
@context[:current_tile_index] = attrs['index'].to_i
|
102
|
+
@context[:current_tile_direction] = attrs['direction'].to_i
|
103
|
+
when 'field'
|
104
|
+
type = FieldType.find_by_key(attrs['type'].to_sym)
|
105
|
+
raise "unexpected field type: #{attrs['type']}. Known types are #{FieldType.map { |t| t.key.to_s }}" if type.nil?
|
129
106
|
x = attrs['x'].to_i
|
130
107
|
y = attrs['y'].to_i
|
108
|
+
points = attrs['points'].to_i
|
109
|
+
index = @context[:current_tile_index]
|
110
|
+
direction = @context[:current_tile_direction]
|
131
111
|
|
132
|
-
@gamestate.board.fields[x
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
112
|
+
@gamestate.board.fields[[x, y]] = Field.new(type, x, y, index, direction, points)
|
113
|
+
when 'lastMove'
|
114
|
+
@gamestate.lastMove = Move.new
|
115
|
+
when 'acceleration'
|
116
|
+
@gamestate.lastMove.add_action_with_order(Acceleration.new(attrs['acc'].to_i), attrs['order'].to_i)
|
117
|
+
when 'advance'
|
118
|
+
@gamestate.lastMove.add_action_with_order(Advance.new(attrs['distance'].to_i), attrs['order'].to_i)
|
119
|
+
when 'turn'
|
120
|
+
@gamestate.lastMove.add_action_with_order(Turn.new(attrs['direction'].to_i), attrs['order'].to_i)
|
121
|
+
when 'push'
|
122
|
+
@gamestate.lastMove.add_action_with_order(Push.new(Direction.find_by_key(attrs['direction'].to_sym)), attrs['order'].to_i)
|
123
|
+
when 'condition'
|
139
124
|
@gamestate.condition = Condition.new(attrs['winner'], attrs['reason'])
|
140
125
|
end
|
141
126
|
end
|
142
127
|
|
128
|
+
# Converts XML attributes for a Player to a new Player object
|
129
|
+
#
|
130
|
+
# @param expectedColor [PlayerColor] Color the player should have. Method will
|
131
|
+
# throw an exception when expectedColor and color in attributes don't match.
|
132
|
+
# @param attributes [Hash] Attributes for the new Player.
|
133
|
+
# @return [Player] The created Player object.
|
134
|
+
def parsePlayer(expectedColor, attributes)
|
135
|
+
player = Player.new(
|
136
|
+
PlayerColor.find_by_key(attributes['color'].to_sym),
|
137
|
+
attributes['displayName']
|
138
|
+
)
|
139
|
+
if player.color != expectedColor
|
140
|
+
throw new IllegalArgumentException("expected #{expectedColor} Player but got #{attributes['color']}")
|
141
|
+
end
|
142
|
+
player.points = attributes['points'].to_i
|
143
|
+
player.direction = Direction.find_by_key(attributes['direction'].to_sym)
|
144
|
+
player.x = attributes['x'].to_i
|
145
|
+
player.y = attributes['y'].to_i
|
146
|
+
player.passengers = attributes['passenger'].to_i
|
147
|
+
player.velocity = attributes['speed'].to_i
|
148
|
+
player.movement = player.velocity
|
149
|
+
player
|
150
|
+
end
|
151
|
+
|
143
152
|
# send a xml document
|
144
153
|
#
|
145
154
|
# @param document [REXML::Document] the document, that will be send to the connected server
|
@@ -147,4 +156,43 @@ class Protocol
|
|
147
156
|
@network.sendXML(document)
|
148
157
|
end
|
149
158
|
|
159
|
+
# send a string
|
160
|
+
#
|
161
|
+
# @param document [String] The string that will be send to the connected server.
|
162
|
+
def sendString(string)
|
163
|
+
@network.sendString("<room roomId=\"#{@roomId}\">#{string}</room>")
|
164
|
+
end
|
165
|
+
|
166
|
+
# Converts a move to XML for sending to the server.
|
167
|
+
#
|
168
|
+
# @param move [Move] The move to convert to XML.
|
169
|
+
def move_to_xml(move)
|
170
|
+
builder = Builder::XmlMarkup.new(indent: 2)
|
171
|
+
builder.data(class: 'move') do |data|
|
172
|
+
move.actions.each_with_index do |action, index|
|
173
|
+
# Converting every action type here instead of requiring the Action
|
174
|
+
# class interface to supply a method which returns the action hash
|
175
|
+
# because XML-generation should be decoupled from internal data
|
176
|
+
# structures.
|
177
|
+
attribute = case action.type
|
178
|
+
when :acceleration
|
179
|
+
{ acc: action.acceleration }
|
180
|
+
when :push, :turn
|
181
|
+
{ direction: action.direction }
|
182
|
+
when :advance
|
183
|
+
{ distance: action.distance }
|
184
|
+
when default
|
185
|
+
raise "unknown action type: #{action.type.inspect}. "\
|
186
|
+
"Can't convert to XML!"
|
187
|
+
end
|
188
|
+
attribute[:order] = index
|
189
|
+
data.tag!(action.type, attribute)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
move.hints.each do |hint|
|
193
|
+
data.hint(content: hint.content)
|
194
|
+
end
|
195
|
+
builder.target!
|
196
|
+
end
|
197
|
+
|
150
198
|
end
|