software_challenge_client 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +2 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/README.md +45 -0
- data/RELEASES.md +3 -0
- data/Rakefile +7 -0
- data/example/client.rb +30 -0
- data/example/main.rb +36 -0
- data/lib/software_challenge_client.rb +17 -0
- data/lib/software_challenge_client/board.rb +315 -0
- data/lib/software_challenge_client/client_interface.rb +7 -0
- data/lib/software_challenge_client/condition.rb +21 -0
- data/lib/software_challenge_client/connection.rb +49 -0
- data/lib/software_challenge_client/debug_hint.rb +33 -0
- data/lib/software_challenge_client/field.rb +42 -0
- data/lib/software_challenge_client/field_type.rb +8 -0
- data/lib/software_challenge_client/game_state.rb +343 -0
- data/lib/software_challenge_client/move.rb +58 -0
- data/lib/software_challenge_client/network.rb +146 -0
- data/lib/software_challenge_client/player.rb +24 -0
- data/lib/software_challenge_client/player_color.rb +23 -0
- data/lib/software_challenge_client/protocol.rb +149 -0
- data/lib/software_challenge_client/runner.rb +33 -0
- data/lib/software_challenge_client/util/constants.rb +5 -0
- data/lib/software_challenge_client/version.rb +3 -0
- data/software_challenge_client.gemspec +24 -0
- metadata +116 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
require_relative 'debug_hint'
|
2
|
+
|
3
|
+
# @author Ralf-Tobias Diekert
|
4
|
+
# A move, that can be performed in twixt
|
5
|
+
class Move
|
6
|
+
# @!attribute [r] x
|
7
|
+
# @return [Integer] x-coordinate
|
8
|
+
attr_reader :x
|
9
|
+
# @!attribute [r] y
|
10
|
+
# @return [Integer] y-coordinate
|
11
|
+
attr_reader :y
|
12
|
+
# @!attribute [r] hints
|
13
|
+
# @return [Array<DebugHint>] the move's hints
|
14
|
+
attr_reader :hints
|
15
|
+
|
16
|
+
# Initializer
|
17
|
+
#
|
18
|
+
# @param x [Integer] x-coordinate
|
19
|
+
# @param y [Integer] y-coordinate
|
20
|
+
def initialize(x, y)
|
21
|
+
@x = x
|
22
|
+
@y = y
|
23
|
+
@hints = Array.new
|
24
|
+
end
|
25
|
+
|
26
|
+
# @overload addHint(hint)
|
27
|
+
# adds a hint to the move
|
28
|
+
# @param hint [DebugHint] the added hint
|
29
|
+
# @overload addHint(key, value)
|
30
|
+
# adds a hint to the move
|
31
|
+
# @param key the added hint's key
|
32
|
+
# @param value the added hint's value
|
33
|
+
# @overload addHint(string)
|
34
|
+
# adds a hint to the move
|
35
|
+
# @param hint [String] the added hint's content
|
36
|
+
def addHint(hint)
|
37
|
+
@hints.push(hint);
|
38
|
+
end
|
39
|
+
|
40
|
+
# adds a hint to the move
|
41
|
+
def addHint(key, value)
|
42
|
+
self.addHint(DebugHint.new(key, value))
|
43
|
+
end
|
44
|
+
|
45
|
+
# adds a hint to the move
|
46
|
+
def addHint(string)
|
47
|
+
self.addHint(DebugHint.new(string))
|
48
|
+
end
|
49
|
+
|
50
|
+
def ==(another_move)
|
51
|
+
return self.x == another_move.x && self.y == another_move.y
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_s
|
55
|
+
return "Move:(#{self.x},#{self.y})"
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require_relative 'protocol'
|
3
|
+
require_relative 'board'
|
4
|
+
require_relative 'client_interface'
|
5
|
+
require 'rexml/document'
|
6
|
+
require 'rexml/element'
|
7
|
+
|
8
|
+
# @author Ralf-Tobias Diekert
|
9
|
+
# This class handles the socket connection to the server
|
10
|
+
class Network
|
11
|
+
@socket
|
12
|
+
@host
|
13
|
+
@port
|
14
|
+
|
15
|
+
@board
|
16
|
+
@client
|
17
|
+
@protocol
|
18
|
+
|
19
|
+
@receiveBuffer
|
20
|
+
@reservationID
|
21
|
+
|
22
|
+
# @!attribute [r] connected
|
23
|
+
# @return [Boolean] true, if the client is connected to a server
|
24
|
+
attr_reader :connected
|
25
|
+
|
26
|
+
def initialize(host, port, board, client)
|
27
|
+
@host, @port, @connected, @board, @client =
|
28
|
+
host, port, false, board, client
|
29
|
+
|
30
|
+
@protocol = Protocol.new(self, @client)
|
31
|
+
@reservationID = ''
|
32
|
+
@receiveBuffer = ''
|
33
|
+
|
34
|
+
puts '> Network/Socket created.'
|
35
|
+
end
|
36
|
+
|
37
|
+
# connects the client with a given server
|
38
|
+
#
|
39
|
+
# @return [Boolean] true, if successfully connected to the server
|
40
|
+
def connect
|
41
|
+
@socket = TCPSocket.open(@host, @port)
|
42
|
+
@connected = true
|
43
|
+
|
44
|
+
self.sendString('<protocol>')
|
45
|
+
if @reservationID != ''
|
46
|
+
document = REXML::Docuent.new
|
47
|
+
element = REXML::Element.new('joinPrepared')
|
48
|
+
element.add_attribute('reservationCode', @reservationID)
|
49
|
+
document.add(element)
|
50
|
+
self.sendXML(document)
|
51
|
+
else
|
52
|
+
document = REXML::Document.new
|
53
|
+
element = REXML::Element.new('join')
|
54
|
+
element.add_attribute('gameType', 'swc_2016_twixt')
|
55
|
+
document.add(element)
|
56
|
+
self.sendXML(document)
|
57
|
+
end
|
58
|
+
return @connected
|
59
|
+
end
|
60
|
+
|
61
|
+
# disconnects the client from a server
|
62
|
+
def disconnect
|
63
|
+
|
64
|
+
if @connected
|
65
|
+
sendString("</protocol>")
|
66
|
+
@connected = false
|
67
|
+
@socket.close
|
68
|
+
end
|
69
|
+
puts '> Disconnected.'
|
70
|
+
end
|
71
|
+
|
72
|
+
# reads from the socket until "</room>" is read
|
73
|
+
def readString
|
74
|
+
puts 'reading'
|
75
|
+
sockMsg = ''
|
76
|
+
if(!@connected)
|
77
|
+
return
|
78
|
+
end
|
79
|
+
|
80
|
+
line =''
|
81
|
+
char = ''
|
82
|
+
while line!="</room>"
|
83
|
+
char = @socket.getc
|
84
|
+
line+=char
|
85
|
+
if char=='\n' || char==' '
|
86
|
+
|
87
|
+
line = ''
|
88
|
+
end
|
89
|
+
sockMsg += char
|
90
|
+
end
|
91
|
+
puts 'ended reading'
|
92
|
+
if sockMsg != ''
|
93
|
+
|
94
|
+
@receiveBuffer.concat(sockMsg)
|
95
|
+
|
96
|
+
# Remove <protocol> tag
|
97
|
+
@receiveBuffer = @receiveBuffer.gsub('<protocol>', '')
|
98
|
+
|
99
|
+
puts 'Receive:'
|
100
|
+
puts ''
|
101
|
+
#puts @receiveBuffer
|
102
|
+
|
103
|
+
# Process text
|
104
|
+
@protocol.processString('<msg>'+@receiveBuffer+'</msg>');
|
105
|
+
self.emptyReceiveBuffer
|
106
|
+
end
|
107
|
+
return true
|
108
|
+
end
|
109
|
+
|
110
|
+
# empties the receive buffer
|
111
|
+
def emptyReceiveBuffer
|
112
|
+
@receiveBuffer = ''
|
113
|
+
end
|
114
|
+
|
115
|
+
# processes an incomming message
|
116
|
+
#
|
117
|
+
# @return [Boolean] true, if the processing of a incomming message was successfull
|
118
|
+
def processMessages
|
119
|
+
if !@connected
|
120
|
+
return false
|
121
|
+
end
|
122
|
+
return self.readString
|
123
|
+
end
|
124
|
+
|
125
|
+
# sends a string to the socket
|
126
|
+
#
|
127
|
+
# @param s [String] the message, to be sent
|
128
|
+
def sendString(s)
|
129
|
+
if(@connected)
|
130
|
+
@socket.print(s);
|
131
|
+
puts 'Send:'
|
132
|
+
puts ''
|
133
|
+
puts(s);
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# sends a xml Document to the buffer
|
138
|
+
#
|
139
|
+
# @param xml [REXML::Docuent] the Document, that will be sent
|
140
|
+
def sendXML(xml)
|
141
|
+
text = ''
|
142
|
+
xml.write(text)
|
143
|
+
self.sendString(text);
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative 'player_color'
|
2
|
+
|
3
|
+
# @author Ralf-Tobias Diekert
|
4
|
+
# A player, participating at a game
|
5
|
+
class Player
|
6
|
+
# @!attribute [r] color
|
7
|
+
# @return [PlayerColor] the player's color
|
8
|
+
attr_reader :color
|
9
|
+
# @!attribute [rw] points
|
10
|
+
# @return [Integer] the player's points
|
11
|
+
attr_accessor :points
|
12
|
+
|
13
|
+
# Initializer
|
14
|
+
# @param the new player's color
|
15
|
+
def initialize(color)
|
16
|
+
@color = color
|
17
|
+
self.points = 0
|
18
|
+
end
|
19
|
+
|
20
|
+
def ==(another_player)
|
21
|
+
return self.color == another_player.color
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# @author Ralf-Tobias Diekert
|
2
|
+
#player color constants
|
3
|
+
module PlayerColor
|
4
|
+
NONE = 1
|
5
|
+
RED = 2
|
6
|
+
BLUE = 4
|
7
|
+
|
8
|
+
# Returns the opponents Color
|
9
|
+
#
|
10
|
+
# @param color [PlayerColor] The player's color, whose opponent needs to be found
|
11
|
+
# @return [PlayerColor] the opponent's color
|
12
|
+
def self.opponentColor(color)
|
13
|
+
if color == PlayerColor::RED
|
14
|
+
return PlayerColor::BLUE
|
15
|
+
end
|
16
|
+
if color == PlayerColor::BLUE
|
17
|
+
return PlayerColor::RED
|
18
|
+
end
|
19
|
+
if color == PlayerColor::NONE
|
20
|
+
return PlayerColor::NONE
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require_relative 'board'
|
3
|
+
require_relative 'move'
|
4
|
+
require_relative 'player'
|
5
|
+
require_relative 'network'
|
6
|
+
require_relative 'connection'
|
7
|
+
require_relative 'client_interface'
|
8
|
+
require 'rexml/document'
|
9
|
+
require 'rexml/streamlistener'
|
10
|
+
|
11
|
+
# @author Ralf-Tobias Diekert
|
12
|
+
# This class handles the parsing of xml strings according to the network protocol of twixt
|
13
|
+
class Protocol
|
14
|
+
include REXML::StreamListener
|
15
|
+
|
16
|
+
# @!attribute [r] gamestate
|
17
|
+
# @return [Gamestate] current gamestate
|
18
|
+
attr_reader :gamestate
|
19
|
+
# @!attribute [rw] roomID
|
20
|
+
# @return [String] current room id
|
21
|
+
attr_accessor :roomID
|
22
|
+
# @!attribute [r] client
|
23
|
+
# @return [ClientInterface] current client
|
24
|
+
attr_reader :client
|
25
|
+
@network
|
26
|
+
|
27
|
+
def initialize(network, client)
|
28
|
+
@gamestate = GameState.new
|
29
|
+
@network, @client = network, client
|
30
|
+
self.client.gamestate = self.gamestate
|
31
|
+
end
|
32
|
+
|
33
|
+
# starts xml-string parsing
|
34
|
+
#
|
35
|
+
# @param text [String] the xml-string that will be parsed
|
36
|
+
def processString(text)
|
37
|
+
list = self
|
38
|
+
#puts "Parse XML:\n#{text}\n----END XML"
|
39
|
+
REXML::Document.parse_stream(text, list)
|
40
|
+
end
|
41
|
+
|
42
|
+
# called if an end-tag is read
|
43
|
+
#
|
44
|
+
# @param name [String] the end-tag name, that was read
|
45
|
+
def tag_end(name)
|
46
|
+
case name
|
47
|
+
when "board"
|
48
|
+
puts @gamestate.board.to_s
|
49
|
+
when "condition"
|
50
|
+
puts "Game ended"
|
51
|
+
@network.disconnect
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# called if a start tag is read
|
56
|
+
# Depending on the tag the gamestate is updated
|
57
|
+
# or the client will be asked for a move
|
58
|
+
#
|
59
|
+
# @param name [String] the start-tag, that was read
|
60
|
+
# @param attrs [Dictionary<String, String>] Attributes attached to the tag
|
61
|
+
def tag_start(name, attrs)
|
62
|
+
case name
|
63
|
+
when "room"
|
64
|
+
@roomID = attrs['roomId']
|
65
|
+
puts "roomId : "+@roomID
|
66
|
+
when "data"
|
67
|
+
puts "data(class) : "+attrs['class']
|
68
|
+
if attrs['class'] == "sc.framework.plugins.protocol.MoveRequest"
|
69
|
+
@client.gamestate = self.gamestate
|
70
|
+
move = @client.getMove
|
71
|
+
document = REXML::Document.new
|
72
|
+
document.add_element('room',{'roomId' => @roomID})
|
73
|
+
data = REXML::Element.new('data')
|
74
|
+
data.add_attribute('class', 'move')
|
75
|
+
data.add_attribute('x', move.x)
|
76
|
+
data.add_attribute('y', move.y)
|
77
|
+
document.root.add_element(data)
|
78
|
+
for h in move.hints
|
79
|
+
hint = REXML::Element.new('hint')
|
80
|
+
hint.add_attribute('content', h.content)
|
81
|
+
document.root.elements['data'].elements << hint
|
82
|
+
end
|
83
|
+
self.sendXml(document)
|
84
|
+
end
|
85
|
+
if attrs['class'] == "error"
|
86
|
+
puts "Game ended - ERROR"
|
87
|
+
puts attrs['message']
|
88
|
+
@network.disconnect
|
89
|
+
end
|
90
|
+
when "state"
|
91
|
+
puts 'new gamestate'
|
92
|
+
@gamestate.turn = attrs['turn'].to_i
|
93
|
+
@gamestate.startPlayerColor = attrs['startPlayer'] == 'RED' ? PlayerColor::RED : PlayerColor::BLUE
|
94
|
+
@gamestate.currentPlayerColor = attrs['currentPlayer'] == 'RED' ? PlayerColor::RED : PlayerColor::BLUE
|
95
|
+
puts "Turn: #{@gamestate.turn}"
|
96
|
+
when "red"
|
97
|
+
puts 'new red player'
|
98
|
+
@gamestate.addPlayer(Player.new(attrs['color'] == 'RED' ? PlayerColor::RED : PlayerColor::BLUE))
|
99
|
+
@gamestate.red.points = attrs['points'].to_i
|
100
|
+
when "blue"
|
101
|
+
puts 'new blue player'
|
102
|
+
@gamestate.addPlayer(Player.new(attrs['color'] == 'RED' ? PlayerColor::RED : PlayerColor::BLUE))
|
103
|
+
@gamestate.blue.points = attrs['points'].to_i
|
104
|
+
when "board"
|
105
|
+
puts 'new board'
|
106
|
+
@gamestate.board = Board.new(true)
|
107
|
+
when "field"
|
108
|
+
type = FieldType::NORMAL
|
109
|
+
ownerColor = PlayerColor::NONE
|
110
|
+
case attrs['type']
|
111
|
+
when 'SWAMP'
|
112
|
+
type = FieldType::SWAMP
|
113
|
+
when 'RED'
|
114
|
+
type = FieldType::RED
|
115
|
+
when 'BLUE'
|
116
|
+
type = FieldType::BLUE
|
117
|
+
when "winner"
|
118
|
+
puts "Game ended"
|
119
|
+
@network.disconnect
|
120
|
+
end
|
121
|
+
|
122
|
+
case attrs['owner']
|
123
|
+
when 'RED'
|
124
|
+
ownerColor = PlayerColor::RED
|
125
|
+
when 'BLUE'
|
126
|
+
ownerColor = PlayerColor::BLUE
|
127
|
+
end
|
128
|
+
x = attrs['x'].to_i
|
129
|
+
y = attrs['y'].to_i
|
130
|
+
|
131
|
+
@gamestate.board.fields[x][y] = Field.new(type, x, y)
|
132
|
+
@gamestate.board.fields[x][y].ownerColor = ownerColor
|
133
|
+
when "connection"
|
134
|
+
@gamestate.board.connections.push(Connection.new(attrs['x1'].to_i, attrs['y1'].to_i, attrs['x2'].to_i, attrs['y2'].to_i, attrs['owner']))
|
135
|
+
when "lastMove"
|
136
|
+
@gamestate.lastMove = Move.new(attrs['x'], attrs['y'])
|
137
|
+
when "condition"
|
138
|
+
@gamestate.condition = Condition.new(attrs['winner'], attrs['reason'])
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# send a xml document
|
143
|
+
#
|
144
|
+
# @param document [REXML::Document] the document, that will be send to the connected server
|
145
|
+
def sendXml(document)
|
146
|
+
@network.sendXML(document)
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require_relative 'board'
|
2
|
+
require_relative 'client_interface'
|
3
|
+
require_relative 'network'
|
4
|
+
|
5
|
+
class Runner
|
6
|
+
attr_reader :network
|
7
|
+
|
8
|
+
def initialize(host, port, client)
|
9
|
+
puts 'Software Challenge 2015'
|
10
|
+
puts 'Ruby Client'
|
11
|
+
puts "Host: #{host}"
|
12
|
+
puts "Port: #{port}"
|
13
|
+
|
14
|
+
board = Board.new(true)
|
15
|
+
@network = Network.new(host, port, board, client)
|
16
|
+
end
|
17
|
+
|
18
|
+
def start
|
19
|
+
self.network.connect
|
20
|
+
if self.network.connected == false
|
21
|
+
puts 'Not connected'
|
22
|
+
return
|
23
|
+
end
|
24
|
+
|
25
|
+
while self.network.connected
|
26
|
+
self.network.processMessages
|
27
|
+
sleep(0.01)
|
28
|
+
end
|
29
|
+
|
30
|
+
puts 'Program end...'
|
31
|
+
self.network.disconnect
|
32
|
+
end
|
33
|
+
end
|